diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index a130b46877..34a3630b78 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -182,7 +183,6 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { } void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) { - auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; selectedDevice = device; @@ -200,32 +200,137 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -void AudioDeviceList::onDevicesChanged(const QList& devices, bool isHMD) { - QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; +// Function returns 'strings similarity' as a number. The lesser number - the more similar strings are. Absolutely equal strings should return 0. +// Optimized version kindly provided by Ken +int levenshteinDistance(const QString& s1, const QString& s2) { + const int m = s1.size(); + const int n = s2.size(); - const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; + if (m == 0) { + return n; + } + if (n == 0) { + return m; + } + + auto cost = (int*)alloca((n + 1) * sizeof(int)); + + for (int j = 0; j <= n; j++) { + cost[j] = j; + } + + for (int i = 0; i < m; i++) { + + int prev = i; + cost[0] = i + 1; + + for (int j = 0; j < n; j++) { + + int temp = cost[j + 1]; + cost[j + 1] = (s1[i] == s2[j]) ? prev : std::min(cost[j], std::min(temp, prev)) + 1; + prev = temp; + } + } + return cost[n]; +} + +std::shared_ptr getSimilarDevice(const QString& deviceName, const QList>& devices) { + + int minDistance = INT_MAX; + int minDistanceIndex = 0; + + for (auto i = 0; i < devices.length(); ++i) { + auto distance = levenshteinDistance(deviceName, devices[i]->info.deviceName()); + if (distance < minDistance) { + minDistance = distance; + minDistanceIndex = i; + } + } + + return devices[minDistanceIndex]; +} + +void AudioDeviceList::onDevicesChanged(const QList& devices) { beginResetModel(); - _devices.clear(); + QList> newDevices; + bool hmdIsSelected = false; + bool desktopIsSelected = false; + + foreach(const QAudioDeviceInfo& deviceInfo, devices) { + for (bool isHMD : {false, true}) { + auto &backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; + if (deviceInfo.deviceName() == backupSelectedDeviceName) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + selectedDevice = deviceInfo; + backupSelectedDeviceName.clear(); + } + } + } foreach(const QAudioDeviceInfo& deviceInfo, devices) { AudioDevice device; - bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; device.info = deviceInfo; device.display = device.info.deviceName() .replace("High Definition", "HD") .remove("Device") .replace(" )", ")"); - if (!selectedDevice.isNull()) { - isSelected = (device.info == selectedDevice); - } else { - //no selected device for context. fallback to saved - isSelected = (device.info.deviceName() == savedDeviceName); + + for (bool isHMD : {false, true}) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; + + if (!selectedDevice.isNull()) { + isSelected = (device.info == selectedDevice); + } + else { + //no selected device for context. fallback to saved + const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; + isSelected = (device.info.deviceName() == savedDeviceName); + } + + if (isSelected) { + if (isHMD) { + hmdIsSelected = isSelected; + } else { + desktopIsSelected = isSelected; + } + + // check if this device *is not* in old devices list - it means it was just re-plugged so needs to be selected explicitly + bool isNewDevice = true; + for (auto& oldDevice : _devices) { + if (oldDevice->info.deviceName() == device.info.deviceName()) { + isNewDevice = false; + break; + } + } + + if (isNewDevice) { + emit selectedDevicePlugged(device.info, isHMD); + } + } } + qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode; - _devices.push_back(newDevice(device)); + newDevices.push_back(newDevice(device)); } + if (!newDevices.isEmpty()) { + if (!hmdIsSelected) { + _backupSelectedHMDDeviceName = !_selectedHMDDevice.isNull() ? _selectedHMDDevice.deviceName() : _hmdSavedDeviceName; + auto device = getSimilarDevice(_backupSelectedHMDDeviceName, newDevices); + device->selectedHMD = true; + emit selectedDevicePlugged(device->info, true); + } + if (!desktopIsSelected) { + _backupSelectedDesktopDeviceName = !_selectedDesktopDevice.isNull() ? _selectedDesktopDevice.deviceName() : _desktopSavedDeviceName; + auto device = getSimilarDevice(_backupSelectedDesktopDeviceName, newDevices); + device->selectedDesktop = true; + emit selectedDevicePlugged(device->info, false); + } + } + + _devices.swap(newDevices); endResetModel(); } @@ -271,12 +376,10 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { // connections are made after client is initialized, so we must also fetch the devices const QList& devicesInput = client->getAudioDevices(QAudio::AudioInput); const QList& devicesOutput = client->getAudioDevices(QAudio::AudioOutput); - //setup HMD devices - _inputs.onDevicesChanged(devicesInput, true); - _outputs.onDevicesChanged(devicesOutput, true); - //setup Desktop devices - _inputs.onDevicesChanged(devicesInput, false); - _outputs.onDevicesChanged(devicesOutput, false); + + //setup devices + _inputs.onDevicesChanged(devicesInput); + _outputs.onDevicesChanged(devicesOutput); } AudioDevices::~AudioDevices() {} @@ -375,11 +478,19 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices, bool isHMD); + void onDevicesChanged(const QList& devices); protected: friend class AudioDevices; @@ -64,6 +65,8 @@ protected: const QAudio::Mode _mode; QAudioDeviceInfo _selectedDesktopDevice; QAudioDeviceInfo _selectedHMDDevice; + QString _backupSelectedDesktopDeviceName; + QString _backupSelectedHMDDeviceName; QList> _devices; QString _hmdSavedDeviceName; QString _desktopSavedDeviceName; @@ -117,13 +120,13 @@ public: AudioDevices(bool& contextIsHMD); virtual ~AudioDevices(); - void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); - void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); - signals: void nop(); private slots: + void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); + void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); + void onContextChanged(const QString& context); void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice, bool isHMD); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 78f55f0d6e..58ec744f4e 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -78,13 +78,17 @@ void WindowScriptingInterface::setFocus() { }); } -void WindowScriptingInterface::raiseMainWindow() { +void WindowScriptingInterface::raise() { // It's forbidden to call raise() from another thread. qApp->postLambdaEvent([] { qApp->raise(); }); } +void WindowScriptingInterface::raiseMainWindow() { + raise(); +} + /// Display an alert box /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 997e425e53..e3b092d011 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -68,9 +68,16 @@ public slots: */ void setFocus(); + /**jsdoc + * Raise the Interface window if it is minimized. If raised, the window gains focus. + * @function Window.raise + */ + void raise(); + /**jsdoc * Raise the Interface window if it is minimized. If raised, the window gains focus. * @function Window.raiseMainWindow + * @deprecated Use {@link Window.raise|raise} instead. */ void raiseMainWindow();