| @@ -1002,11 +1002,17 @@ public: | |||
| // ------------------------------------------------------------------- | |||
| // Misc | |||
| /*! | |||
| * Check if the engine is about to close. | |||
| */ | |||
| bool isAboutToClose() const noexcept; | |||
| /*! | |||
| * Tell the engine it's about to close. | |||
| * This is used to prevent the engine thread(s) from reactivating. | |||
| * Returns true if there's no pending engine events. | |||
| */ | |||
| void setAboutToClose() noexcept; | |||
| bool setAboutToClose() noexcept; | |||
| // ------------------------------------------------------------------- | |||
| // Options | |||
| @@ -340,8 +340,9 @@ CARLA_EXPORT bool carla_is_engine_running(); | |||
| /*! | |||
| * Tell the engine it's about to close. | |||
| * This is used to prevent the engine thread(s) from reactivating. | |||
| * Returns true if there's no pending engine events. | |||
| */ | |||
| CARLA_EXPORT void carla_set_engine_about_to_close(); | |||
| CARLA_EXPORT bool carla_set_engine_about_to_close(); | |||
| /*! | |||
| * Set the engine callback function. | |||
| @@ -421,12 +421,12 @@ bool carla_is_engine_running() | |||
| return (gStandalone.engine != nullptr && gStandalone.engine->isRunning()); | |||
| } | |||
| void carla_set_engine_about_to_close() | |||
| bool carla_set_engine_about_to_close() | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,); | |||
| CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, true); | |||
| carla_debug("carla_set_engine_about_to_close()"); | |||
| gStandalone.engine->setAboutToClose(); | |||
| return gStandalone.engine->setAboutToClose(); | |||
| } | |||
| void carla_set_engine_callback(EngineCallbackFunc func, void* ptr) | |||
| @@ -1340,11 +1340,21 @@ void CarlaEngine::setLastError(const char* const error) const noexcept | |||
| pData->lastError = error; | |||
| } | |||
| void CarlaEngine::setAboutToClose() noexcept | |||
| // ----------------------------------------------------------------------- | |||
| // Misc | |||
| bool CarlaEngine::isAboutToClose() const noexcept | |||
| { | |||
| return pData->aboutToClose; | |||
| } | |||
| bool CarlaEngine::setAboutToClose() noexcept | |||
| { | |||
| carla_debug("CarlaEngine::setAboutToClose()"); | |||
| pData->aboutToClose = true; | |||
| return (pData->isIdling == 0); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1856,6 +1866,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| xmlElement = xmlDoc.getDocumentElement(false); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(xmlElement != nullptr, "Failed to completely parse project file"); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| const bool isPlugin(getType() == kEngineTypePlugin); | |||
| // engine settings | |||
| @@ -1968,6 +1981,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| break; | |||
| } | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| // handle plugins first | |||
| for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) | |||
| { | |||
| @@ -1980,6 +1996,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr); | |||
| const void* extraStuff = nullptr; | |||
| @@ -2004,6 +2023,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| { | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| #ifndef BUILD_BRIDGE | |||
| // deactivate bridge client-side ping check, since some plugins block during load | |||
| if ((plugin->getHints() & PLUGIN_IS_BRIDGE) != 0 && ! isPreset) | |||
| @@ -2034,6 +2056,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| // handle connections (internal) | |||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||
| { | |||
| @@ -2076,6 +2101,9 @@ bool CarlaEngine::loadProjectInternal(juce::XmlDocument& xmlDoc) | |||
| } | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| } | |||
| // if we're running inside some session-manager (and using JACK), let them handle the external connections | |||
| @@ -2567,6 +2567,8 @@ public: | |||
| if (fInitiated) | |||
| break; | |||
| if (pData->engine->isAboutToClose()) | |||
| break; | |||
| carla_msleep(20); | |||
| } | |||
| @@ -1852,7 +1852,7 @@ class CarlaHostNull(CarlaHostMeta): | |||
| return False | |||
| def set_engine_about_to_close(self): | |||
| return | |||
| return True | |||
| def set_engine_callback(self, func): | |||
| self.fEngineCallback = func | |||
| @@ -2119,7 +2119,7 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| self.lib.carla_is_engine_running.restype = c_bool | |||
| self.lib.carla_set_engine_about_to_close.argtypes = None | |||
| self.lib.carla_set_engine_about_to_close.restype = None | |||
| self.lib.carla_set_engine_about_to_close.restype = c_bool | |||
| self.lib.carla_set_engine_callback.argtypes = [EngineCallbackFunc, c_void_p] | |||
| self.lib.carla_set_engine_callback.restype = None | |||
| @@ -2382,7 +2382,7 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| return bool(self.lib.carla_is_engine_running()) | |||
| def set_engine_about_to_close(self): | |||
| self.lib.carla_set_engine_about_to_close() | |||
| return bool(self.lib.carla_set_engine_about_to_close()) | |||
| def set_engine_callback(self, func): | |||
| self._engineCallback = EngineCallbackFunc(func) | |||
| @@ -111,6 +111,10 @@ class HostWindow(QMainWindow): | |||
| self.fIsProjectLoading = False | |||
| self.fCurrentlyRemovingAllPlugins = False | |||
| # run a custom action after engine is properly closed | |||
| # 1 for close carla, 2 for load project | |||
| self.fCustomStopAction = 0 | |||
| # first attempt of auto-start engine doesn't show an error | |||
| self.fFirstEngineInit = True | |||
| @@ -623,22 +627,44 @@ class HostWindow(QMainWindow): | |||
| @pyqtSlot() | |||
| def slot_engineStop(self, forced = False): | |||
| if self.fPluginCount > 0: | |||
| if not forced: | |||
| ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" | |||
| "Do you want to do this now?"), | |||
| QMessageBox.Yes | QMessageBox.No, QMessageBox.No) | |||
| if ask != QMessageBox.Yes: | |||
| return | |||
| self.killTimers() | |||
| self.removeAllPlugins() | |||
| self.host.set_engine_about_to_close() | |||
| self.host.remove_all_plugins() | |||
| if self.fPluginCount == 0: | |||
| self.engineStopFinal() | |||
| return True | |||
| if not forced: | |||
| ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n" | |||
| "Do you want to do this now?"), | |||
| QMessageBox.Yes | QMessageBox.No, QMessageBox.No) | |||
| if ask != QMessageBox.Yes: | |||
| return | |||
| return self.slot_engineStopTryAgain() | |||
| @pyqtSlot() | |||
| def slot_engineStopTryAgain(self): | |||
| if self.host.is_engine_running() and not self.host.set_engine_about_to_close(): | |||
| QTimer.singleShot(0, self.slot_engineStopTryAgain) | |||
| return False | |||
| self.engineStopFinal() | |||
| return True | |||
| def engineStopFinal(self): | |||
| self.killTimers() | |||
| self.removeAllPlugins() | |||
| self.host.remove_all_plugins() | |||
| if self.host.is_engine_running() and not self.host.engine_close(): | |||
| print(self.host.get_last_error()) | |||
| if self.fCustomStopAction == 1: | |||
| self.close() | |||
| elif self.fCustomStopAction == 2: | |||
| self.slot_engineStart() | |||
| self.loadProjectNow() | |||
| self.fCustomStopAction = 0 | |||
| # -------------------------------------------------------------------------------------------------------- | |||
| # Engine (host callbacks) | |||
| @@ -1683,7 +1709,11 @@ class HostWindow(QMainWindow): | |||
| self.fClientName = os.path.basename(valueStr) | |||
| self.fProjectFilename = QFileInfo(valueStr+".carxp").absoluteFilePath() | |||
| self.setProperWindowTitle() | |||
| self.slot_engineStop(True) | |||
| self.fCustomStopAction = 2 | |||
| if not self.slot_engineStop(True): | |||
| return | |||
| self.slot_engineStart() | |||
| self.loadProjectNow() | |||
| @@ -1733,7 +1763,8 @@ class HostWindow(QMainWindow): | |||
| @pyqtSlot() | |||
| def slot_handleSIGTERM(self): | |||
| print("Got SIGTERM -> Closing now") | |||
| self.close() | |||
| self.fCustomStopAction = 1 | |||
| self.slot_engineStop(True) | |||
| # -------------------------------------------------------------------------------------------------------- | |||
| # Internal stuff | |||
| @@ -1817,6 +1848,11 @@ class HostWindow(QMainWindow): | |||
| return pitem.getWidget() | |||
| # -------------------------------------------------------------------------------------------------------- | |||
| def waitForPendingEvents(self): | |||
| pass | |||
| # -------------------------------------------------------------------------------------------------------- | |||
| # show/hide event | |||
| @@ -1927,7 +1963,10 @@ class HostWindow(QMainWindow): | |||
| self.saveSettings() | |||
| if self.host.is_engine_running() and not (self.host.isControl or self.host.isPlugin): | |||
| self.slot_engineStop(True) | |||
| if not self.slot_engineStop(True): | |||
| self.fCustomStopAction = 1 | |||
| event.accept() | |||
| return | |||
| QMainWindow.closeEvent(self, event) | |||
| @@ -64,7 +64,7 @@ class PluginHost(CarlaHostQtPlugin): | |||
| return self.fExternalUI.isRunning() | |||
| def set_engine_about_to_close(self): | |||
| return | |||
| return True | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Main Window | |||