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