We can now timeout forever, as we let the user cancel when he wants to Signed-off-by: falkTX <falktx@gmail.com>tags/v2.1-alpha2
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Carla Plugin Host | |||
| * Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| @@ -941,40 +941,48 @@ typedef enum { | |||
| */ | |||
| ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34, | |||
| /*! | |||
| * A cancelable action has been started or stopped. | |||
| * @a pluginId Plugin Id the action relates to, -1 for none | |||
| * @a value1 1 for action started, 0 for stopped | |||
| * @a valueStr Action name | |||
| */ | |||
| ENGINE_CALLBACK_CANCELABLE_ACTION = 35, | |||
| /*! | |||
| * Project has finished loading. | |||
| */ | |||
| ENGINE_CALLBACK_PROJECT_LOAD_FINISHED = 35, | |||
| ENGINE_CALLBACK_PROJECT_LOAD_FINISHED = 36, | |||
| /*! | |||
| * NSM callback. | |||
| * (Work in progress, values are not defined yet) | |||
| */ | |||
| ENGINE_CALLBACK_NSM = 36, | |||
| ENGINE_CALLBACK_NSM = 37, | |||
| /*! | |||
| * Idle frontend. | |||
| * This is used by the engine during long operations that might block the frontend, | |||
| * giving it the possibility to idle while the operation is still in place. | |||
| */ | |||
| ENGINE_CALLBACK_IDLE = 37, | |||
| ENGINE_CALLBACK_IDLE = 38, | |||
| /*! | |||
| * Show a message as information. | |||
| * @a valueStr The message | |||
| */ | |||
| ENGINE_CALLBACK_INFO = 38, | |||
| ENGINE_CALLBACK_INFO = 39, | |||
| /*! | |||
| * Show a message as an error. | |||
| * @a valueStr The message | |||
| */ | |||
| ENGINE_CALLBACK_ERROR = 39, | |||
| ENGINE_CALLBACK_ERROR = 40, | |||
| /*! | |||
| * The engine has crashed or malfunctioned and will no longer work. | |||
| */ | |||
| ENGINE_CALLBACK_QUIT = 40 | |||
| ENGINE_CALLBACK_QUIT = 41 | |||
| } EngineCallbackOpcode; | |||
| @@ -1065,6 +1065,17 @@ public: | |||
| bool isLoadingProject() const noexcept; | |||
| #endif | |||
| /*! | |||
| * Tell the engine to stop the current cancelable action. | |||
| * @see ENGINE_CALLBACK_CANCELABLE_ACTION | |||
| */ | |||
| void setActionCanceled(const bool canceled) noexcept; | |||
| /*! | |||
| * Check wherever the last cancelable action was indeed canceled or not. | |||
| */ | |||
| bool wasActionCanceled() const noexcept; | |||
| // ------------------------------------------------------------------- | |||
| // Options | |||
| @@ -353,6 +353,12 @@ CARLA_EXPORT void carla_engine_idle(); | |||
| */ | |||
| CARLA_EXPORT bool carla_is_engine_running(); | |||
| /*! | |||
| * Tell the engine to stop the current cancelable action. | |||
| * @see ENGINE_CALLBACK_CANCELABLE_ACTION | |||
| */ | |||
| CARLA_EXPORT void carla_cancel_engine_action(); | |||
| /*! | |||
| * Tell the engine it's about to close. | |||
| * This is used to prevent the engine thread(s) from reactivating. | |||
| @@ -537,6 +537,12 @@ bool carla_is_engine_running() | |||
| return (gStandalone.engine != nullptr && gStandalone.engine->isRunning()); | |||
| } | |||
| void carla_cancel_engine_action() | |||
| { | |||
| if (gStandalone.engine != nullptr) | |||
| gStandalone.engine->setActionCanceled(true); | |||
| } | |||
| bool carla_set_engine_about_to_close() | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, true); | |||
| @@ -1390,6 +1390,16 @@ bool CarlaEngine::isLoadingProject() const noexcept | |||
| } | |||
| #endif | |||
| void CarlaEngine::setActionCanceled(const bool canceled) noexcept | |||
| { | |||
| pData->actionCanceled = canceled; | |||
| } | |||
| bool CarlaEngine::wasActionCanceled() const noexcept | |||
| { | |||
| return pData->actionCanceled; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // Global options | |||
| @@ -2053,17 +2063,28 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| return false; | |||
| } | |||
| pData->actionCanceled = false; | |||
| callback(ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 1, 0, 0.0f, "Loading project"); | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| const ScopedValueSetter<bool> _svs(pData->loadingProject, true, false); | |||
| const ScopedValueSetter<bool> _svs2(pData->loadingProject, true, false); | |||
| #endif | |||
| // completely load file | |||
| xmlElement = xmlDoc.getDocumentElement(false); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(xmlElement != nullptr, "Failed to completely parse project file"); | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| const bool isPlugin(getType() == kEngineTypePlugin); | |||
| // load engine settings first of all | |||
| @@ -2177,10 +2198,18 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| setOption(static_cast<EngineOption>(option), value, valueStr); | |||
| } | |||
| } | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| } | |||
| // now setup transport | |||
| if (XmlElement* const elem = (isPreset || isPlugin) ? nullptr : xmlElement->getChildByName("Transport")) | |||
| @@ -2193,12 +2222,20 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| // some sane limits | |||
| if (bpm >= 20.0 && bpm < 400.0) | |||
| pData->time.setBPM(bpm); | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| // and we handle plugins | |||
| for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) | |||
| { | |||
| @@ -2214,6 +2251,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr); | |||
| #ifndef BUILD_BRIDGE | |||
| @@ -2232,6 +2275,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| String lsState; | |||
| lsState << "0.35\n"; | |||
| lsState << "18 0 Chromatic\n"; | |||
| @@ -2377,6 +2426,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| // deactivate bridge client-side ping check, since some plugins block during load | |||
| if ((plugin->getHints() & PLUGIN_IS_BRIDGE) != 0 && ! isPreset) | |||
| plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "false", false); | |||
| @@ -2411,6 +2466,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (isPreset) | |||
| { | |||
| callback(ENGINE_CALLBACK_PROJECT_LOAD_FINISHED, 0, 0, 0, 0.0f, nullptr); | |||
| callback(ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 0, 0, 0.0f, "Loading project"); | |||
| return true; | |||
| } | |||
| } | |||
| @@ -2430,6 +2486,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| bool hasInternalConnections = false; | |||
| // and now we handle connections (internal) | |||
| @@ -2471,6 +2533,12 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| if (pData->aboutToClose) | |||
| return true; | |||
| if (pData->actionCanceled) | |||
| { | |||
| setLastError("Project load canceled"); | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| @@ -2541,10 +2609,10 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||
| break; | |||
| } | |||
| } | |||
| #endif | |||
| callback(ENGINE_CALLBACK_PROJECT_LOAD_FINISHED, 0, 0, 0, 0.0f, nullptr); | |||
| callback(ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 0, 0, 0.0f, "Loading project"); | |||
| return true; | |||
| } | |||
| @@ -372,6 +372,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept | |||
| callbackPtr(nullptr), | |||
| fileCallback(nullptr), | |||
| fileCallbackPtr(nullptr), | |||
| actionCanceled(false), | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| loadingProject(false), | |||
| #endif | |||
| @@ -219,6 +219,8 @@ struct CarlaEngine::ProtectedData { | |||
| FileCallbackFunc fileCallback; | |||
| void* fileCallbackPtr; | |||
| bool actionCanceled; | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| bool loadingProject; | |||
| #endif | |||
| @@ -152,6 +152,10 @@ protected: | |||
| if (valueStr != nullptr) | |||
| delete[] valueStr; | |||
| } | |||
| else if (std::strcmp(msg, "cancel_engine_action") == 0) | |||
| { | |||
| fEngine->setActionCanceled(true); | |||
| } | |||
| else if (std::strcmp(msg, "load_file") == 0) | |||
| { | |||
| const char* filename; | |||
| @@ -387,7 +387,6 @@ public: | |||
| fTimedError(false), | |||
| fBufferSize(engine->getBufferSize()), | |||
| fProcWaitTime(0), | |||
| fLastPongTime(-1), | |||
| fBridgeBinary(), | |||
| fBridgeThread(engine, this), | |||
| fShmAudioPool(), | |||
| @@ -1790,9 +1789,6 @@ public: | |||
| carla_debug("CarlaPluginBridge::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(opcode)); | |||
| } | |||
| #endif | |||
| if (opcode != kPluginBridgeNonRtServerNull && fLastPongTime > 0) | |||
| fLastPongTime = Time::currentTimeMillis(); | |||
| switch (opcode) | |||
| { | |||
| case kPluginBridgeNonRtServerNull: | |||
| @@ -2436,8 +2432,6 @@ private: | |||
| uint fBufferSize; | |||
| uint fProcWaitTime; | |||
| int64_t fLastPongTime; | |||
| CarlaString fBridgeBinary; | |||
| CarlaPluginBridgeThread fBridgeThread; | |||
| @@ -2664,24 +2658,18 @@ private: | |||
| fBridgeThread.startThread(); | |||
| fLastPongTime = Time::currentTimeMillis(); | |||
| CARLA_SAFE_ASSERT(fLastPongTime > 0); | |||
| static bool sFirstInit = true; | |||
| int64_t timeoutEnd = 5000; | |||
| const bool needsEngineIdle = pData->engine->getType() != kEngineTypePlugin; | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| const bool needsCancelableAction = ! pData->engine->isLoadingProject(); | |||
| if (sFirstInit) | |||
| timeoutEnd *= 2; | |||
| #ifndef CARLA_OS_WIN | |||
| if (fBinaryType == BINARY_WIN32 || fBinaryType == BINARY_WIN64) | |||
| timeoutEnd *= 2; | |||
| if (needsCancelableAction) | |||
| { | |||
| pData->engine->setActionCanceled(false); | |||
| pData->engine->callback(ENGINE_CALLBACK_CANCELABLE_ACTION, pData->id, 1, 0, 0.0f, "Loading JACK application"); | |||
| } | |||
| #endif | |||
| sFirstInit = false; | |||
| const bool needsEngineIdle = pData->engine->getType() != kEngineTypePlugin; | |||
| for (; Time::currentTimeMillis() < fLastPongTime + timeoutEnd && fBridgeThread.isThreadRunning();) | |||
| for (;fBridgeThread.isThreadRunning();) | |||
| { | |||
| pData->engine->callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| @@ -2692,13 +2680,16 @@ private: | |||
| if (fInitiated) | |||
| break; | |||
| if (pData->engine->isAboutToClose()) | |||
| if (pData->engine->isAboutToClose() || pData->engine->wasActionCanceled()) | |||
| break; | |||
| carla_msleep(5); | |||
| } | |||
| fLastPongTime = -1; | |||
| #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
| if (needsCancelableAction) | |||
| pData->engine->callback(ENGINE_CALLBACK_CANCELABLE_ACTION, pData->id, 0, 0, 0.0f, "Loading JACK application"); | |||
| #endif | |||
| if (fInitError || ! fInitiated) | |||
| { | |||
| @@ -205,7 +205,6 @@ public: | |||
| fProcCanceled(false), | |||
| fBufferSize(engine->getBufferSize()), | |||
| fProcWaitTime(0), | |||
| fLastPingTime(-1), | |||
| fBridgeThread(engine, this), | |||
| fShmAudioPool(), | |||
| fShmRtClientControl(), | |||
| @@ -426,14 +425,6 @@ public: | |||
| try { | |||
| handleNonRtData(); | |||
| } CARLA_SAFE_EXCEPTION("handleNonRtData"); | |||
| if (fLastPingTime > 0 && Time::currentTimeMillis() > fLastPingTime + 30000) | |||
| { | |||
| carla_stderr("Did not receive ping message from server for 30 secs, closing..."); | |||
| // TODO | |||
| //threadShouldExit(); | |||
| //callback(ENGINE_CALLBACK_QUIT, 0, 0, 0, 0.0f, nullptr); | |||
| } | |||
| } | |||
| else if (fInitiated) | |||
| { | |||
| @@ -1132,9 +1123,6 @@ public: | |||
| carla_debug("CarlaPluginJack::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(opcode)); | |||
| } | |||
| //#endif | |||
| if (opcode != kPluginBridgeNonRtServerNull && fLastPingTime > 0) | |||
| fLastPingTime = Time::currentTimeMillis(); | |||
| switch (opcode) | |||
| { | |||
| case kPluginBridgeNonRtServerNull: | |||
| @@ -1372,8 +1360,6 @@ private: | |||
| uint fBufferSize; | |||
| uint fProcWaitTime; | |||
| int64_t fLastPingTime; | |||
| CarlaPluginJackThread fBridgeThread; | |||
| BridgeAudioPool fShmAudioPool; | |||
| @@ -1486,20 +1472,16 @@ private: | |||
| fBridgeThread.startThread(); | |||
| fLastPingTime = Time::currentTimeMillis(); | |||
| CARLA_SAFE_ASSERT(fLastPingTime > 0); | |||
| static bool sFirstInit = true; | |||
| int64_t timeoutEnd = 5000; | |||
| if (sFirstInit) | |||
| timeoutEnd *= 2; | |||
| sFirstInit = false; | |||
| const bool needsCancelableAction = ! pData->engine->isLoadingProject(); | |||
| const bool needsEngineIdle = pData->engine->getType() != kEngineTypePlugin; | |||
| for (; Time::currentTimeMillis() < fLastPingTime + timeoutEnd && fBridgeThread.isThreadRunning();) | |||
| if (needsCancelableAction) | |||
| { | |||
| pData->engine->setActionCanceled(false); | |||
| pData->engine->callback(ENGINE_CALLBACK_CANCELABLE_ACTION, pData->id, 1, 0, 0.0f, "Loading JACK application"); | |||
| } | |||
| for (;fBridgeThread.isThreadRunning();) | |||
| { | |||
| pData->engine->callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| @@ -1510,13 +1492,14 @@ private: | |||
| if (fInitiated) | |||
| break; | |||
| if (pData->engine->isAboutToClose()) | |||
| if (pData->engine->isAboutToClose() || pData->engine->wasActionCanceled()) | |||
| break; | |||
| carla_msleep(20); | |||
| carla_msleep(5); | |||
| } | |||
| fLastPingTime = -1; | |||
| if (needsCancelableAction) | |||
| pData->engine->callback(ENGINE_CALLBACK_CANCELABLE_ACTION, pData->id, 0, 0, 0.0f, "Loading JACK application"); | |||
| if (fInitError || ! fInitiated) | |||
| { | |||
| @@ -691,28 +691,34 @@ ENGINE_CALLBACK_BUFFER_SIZE_CHANGED = 33 | |||
| # @a value3 New sample rate | |||
| ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34 | |||
| # A cancelable action has been started or stopped. | |||
| # @a pluginId Plugin Id the action relates to, -1 for none | |||
| # @a value1 1 for action started, 0 for stopped | |||
| # @a valueStr Action name | |||
| ENGINE_CALLBACK_CANCELABLE_ACTION = 35 | |||
| # Project has finished loading. | |||
| ENGINE_CALLBACK_PROJECT_LOAD_FINISHED = 35 | |||
| ENGINE_CALLBACK_PROJECT_LOAD_FINISHED = 36 | |||
| # NSM callback. | |||
| # (Work in progress, values are not defined yet) | |||
| ENGINE_CALLBACK_NSM = 36 | |||
| ENGINE_CALLBACK_NSM = 37 | |||
| # Idle frontend. | |||
| # This is used by the engine during long operations that might block the frontend, | |||
| # giving it the possibility to idle while the operation is still in place. | |||
| ENGINE_CALLBACK_IDLE = 37 | |||
| ENGINE_CALLBACK_IDLE = 38 | |||
| # Show a message as information. | |||
| # @a valueStr The message | |||
| ENGINE_CALLBACK_INFO = 38 | |||
| ENGINE_CALLBACK_INFO = 39 | |||
| # Show a message as an error. | |||
| # @a valueStr The message | |||
| ENGINE_CALLBACK_ERROR = 39 | |||
| ENGINE_CALLBACK_ERROR = 40 | |||
| # The engine has crashed or malfunctioned and will no longer work. | |||
| ENGINE_CALLBACK_QUIT = 40 | |||
| ENGINE_CALLBACK_QUIT = 41 | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Engine Option | |||
| @@ -1330,6 +1336,10 @@ class CarlaHostMeta(object): | |||
| def is_engine_running(self): | |||
| raise NotImplementedError | |||
| @abstractmethod | |||
| def cancel_engine_action(self): | |||
| raise NotImplementedError | |||
| # Tell the engine it's about to close. | |||
| # This is used to prevent the engine thread(s) from reactivating. | |||
| @abstractmethod | |||
| @@ -1934,6 +1944,9 @@ class CarlaHostNull(CarlaHostMeta): | |||
| def is_engine_running(self): | |||
| return self.fEngineRunning | |||
| def cancel_engine_action(self): | |||
| return | |||
| def set_engine_about_to_close(self): | |||
| return True | |||
| @@ -2219,6 +2232,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| self.lib.carla_is_engine_running.argtypes = None | |||
| self.lib.carla_is_engine_running.restype = c_bool | |||
| self.lib.carla_cancel_engine_action.argtypes = None | |||
| self.lib.carla_cancel_engine_action.restype = None | |||
| self.lib.carla_set_engine_about_to_close.argtypes = None | |||
| self.lib.carla_set_engine_about_to_close.restype = c_bool | |||
| @@ -2494,6 +2510,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
| def is_engine_running(self): | |||
| return bool(self.lib.carla_is_engine_running()) | |||
| def cancel_engine_action(self): | |||
| return self.lib.carla_cancel_engine_action() | |||
| def set_engine_about_to_close(self): | |||
| return bool(self.lib.carla_set_engine_about_to_close()) | |||
| @@ -2839,6 +2858,9 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
| def get_engine_driver_device_info(self, index, name): | |||
| return PyEngineDriverDeviceInfo | |||
| def cancel_engine_action(self): | |||
| self.sendMsg(["cancel_engine_action"]) | |||
| def set_engine_callback(self, func): | |||
| return # TODO | |||
| @@ -66,6 +66,7 @@ class CarlaHostSignals(QObject): | |||
| TransportModeChangedCallback = pyqtSignal(int, str) | |||
| BufferSizeChangedCallback = pyqtSignal(int) | |||
| SampleRateChangedCallback = pyqtSignal(float) | |||
| CancelableActionCallback = pyqtSignal(int, bool, str) | |||
| ProjectLoadFinishedCallback = pyqtSignal() | |||
| NSMCallback = pyqtSignal(int, int, str) | |||
| InfoCallback = pyqtSignal(str) | |||
| @@ -148,6 +148,9 @@ class HostWindow(QMainWindow): | |||
| if MACOS: | |||
| self.fMacClosingHelper = True | |||
| # CancelableActionCallback Box | |||
| self.fCancelableActionBox = None | |||
| # run a custom action after engine is properly closed | |||
| self.fCustomStopAction = self.CUSTOM_ACTION_NONE | |||
| @@ -492,6 +495,7 @@ class HostWindow(QMainWindow): | |||
| host.EngineStoppedCallback.connect(self.slot_handleEngineStoppedCallback) | |||
| host.TransportModeChangedCallback.connect(self.slot_handleTransportModeChangedCallback) | |||
| host.SampleRateChangedCallback.connect(self.slot_handleSampleRateChangedCallback) | |||
| host.CancelableActionCallback.connect(self.slot_handleCancelableActionCallback) | |||
| host.ProjectLoadFinishedCallback.connect(self.slot_handleProjectLoadFinishedCallback) | |||
| host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback) | |||
| @@ -659,9 +663,14 @@ class HostWindow(QMainWindow): | |||
| self.projectLoadingStarted() | |||
| self.fIsProjectLoading = True | |||
| self.host.load_project(self.fProjectFilename) | |||
| # Project finished loading is sent by engine | |||
| if not self.host.load_project(self.fProjectFilename): | |||
| self.fIsProjectLoading = False | |||
| self.projectLoadingFinished() | |||
| CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load project"), | |||
| self.host.get_last_error(), | |||
| QMessageBox.Ok, QMessageBox.Ok) | |||
| def loadProjectLater(self, filename): | |||
| self.fProjectFilename = QFileInfo(filename).absoluteFilePath() | |||
| @@ -672,7 +681,11 @@ class HostWindow(QMainWindow): | |||
| if not self.fProjectFilename: | |||
| return qCritical("ERROR: saving project without filename set") | |||
| self.host.save_project(self.fProjectFilename) | |||
| if not self.host.save_project(self.fProjectFilename): | |||
| CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to save project"), | |||
| self.host.get_last_error(), | |||
| QMessageBox.Ok, QMessageBox.Ok) | |||
| return | |||
| if not self.fWithCanvas: | |||
| return | |||
| @@ -943,6 +956,29 @@ class HostWindow(QMainWindow): | |||
| self.fSampleRate = newSampleRate | |||
| self.refreshTransport(True) | |||
| @pyqtSlot(int, bool, str) | |||
| def slot_handleCancelableActionCallback(self, pluginId, started, action): | |||
| if self.fCancelableActionBox is not None: | |||
| self.fCancelableActionBox.close() | |||
| if started: | |||
| self.fCancelableActionBox = QMessageBox(self) | |||
| self.fCancelableActionBox.setIcon(QMessageBox.Critical) | |||
| self.fCancelableActionBox.setWindowTitle(self.tr("Action in progress")) | |||
| self.fCancelableActionBox.setText(action) | |||
| self.fCancelableActionBox.setInformativeText(self.tr("An action is in progress, please wait...")) | |||
| self.fCancelableActionBox.setStandardButtons(QMessageBox.Cancel) | |||
| self.fCancelableActionBox.setDefaultButton(QMessageBox.Cancel) | |||
| self.fCancelableActionBox.buttonClicked.connect(self.slot_canlableActionBoxClicked) | |||
| self.fCancelableActionBox.show() | |||
| else: | |||
| self.fCancelableActionBox = None | |||
| @pyqtSlot() | |||
| def slot_canlableActionBoxClicked(self): | |||
| self.host.cancel_engine_action() | |||
| @pyqtSlot() | |||
| def slot_handleProjectLoadFinishedCallback(self): | |||
| self.fIsProjectLoading = False | |||
| @@ -2577,7 +2613,10 @@ def engineCallback(host, action, pluginId, value1, value2, value3, valueStr): | |||
| host.BufferSizeChangedCallback.emit(value1) | |||
| elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED: | |||
| host.SampleRateChangedCallback.emit(value3) | |||
| elif action == ENGINE_CALLBACK_CANCELABLE_ACTION: | |||
| host.CancelableActionCallback.emit(pluginId, bool(value1 != 0), valueStr) | |||
| elif action == ENGINE_CALLBACK_PROJECT_LOAD_FINISHED: | |||
| print("ProjectLoadFinishedCallback") | |||
| host.ProjectLoadFinishedCallback.emit() | |||
| elif action == ENGINE_CALLBACK_NSM: | |||
| host.NSMCallback.emit(value1, value2, valueStr) | |||
| @@ -270,6 +270,8 @@ const char* EngineCallbackOpcode2Str(const EngineCallbackOpcode opcode) noexcept | |||
| return "ENGINE_CALLBACK_BUFFER_SIZE_CHANGED"; | |||
| case ENGINE_CALLBACK_SAMPLE_RATE_CHANGED: | |||
| return "ENGINE_CALLBACK_SAMPLE_RATE_CHANGED"; | |||
| case ENGINE_CALLBACK_CANCELABLE_ACTION: | |||
| return "ENGINE_CALLBACK_CANCELABLE_ACTION"; | |||
| case ENGINE_CALLBACK_PROJECT_LOAD_FINISHED: | |||
| return "ENGINE_CALLBACK_PROJECT_LOAD_FINISHED"; | |||
| case ENGINE_CALLBACK_NSM: | |||