diff --git a/source/backend/engine/CarlaEngineGraph.cpp b/source/backend/engine/CarlaEngineGraph.cpp index 4d6b5bb81..056c8757e 100644 --- a/source/backend/engine/CarlaEngineGraph.cpp +++ b/source/backend/engine/CarlaEngineGraph.cpp @@ -2831,7 +2831,8 @@ bool CarlaEngine::patchbayDisconnect(const bool external, const uint connectionI bool CarlaEngine::patchbaySetGroupPos(const bool sendHost, const bool sendOSC, const bool external, const uint groupId, const int x1, const int y1, const int x2, const int y2) { - CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false); + CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || + pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false); CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false); carla_debug("CarlaEngine::patchbaySetGroupPos(%u, %i, %i, %i, %i)", groupId, x1, y1, x2, y2); diff --git a/source/backend/engine/CarlaEngineJack.cpp b/source/backend/engine/CarlaEngineJack.cpp index 54ff8711f..e770cc3d8 100644 --- a/source/backend/engine/CarlaEngineJack.cpp +++ b/source/backend/engine/CarlaEngineJack.cpp @@ -1244,6 +1244,7 @@ public: fUsedGroups(), fUsedPorts(), fUsedConnections(), + fThreadSafeMetadataMutex(), fPatchbayProcThreadProtectionMutex(), fRetConns(), fPostPonedEvents(), @@ -1281,7 +1282,8 @@ public: uint getMaxClientNameSize() const noexcept override { #ifndef BUILD_BRIDGE - if (pData->options.processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT || pData->options.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS) + if (pData->options.processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT || + pData->options.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS) #endif { try { @@ -1430,27 +1432,31 @@ public: } } - if (char* const uuidstr = jackbridge_client_get_uuid(fClient)) { - jack_uuid_t uuid; + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); - if (jackbridge_uuid_parse(uuidstr, &uuid)) + if (char* const uuidstr = jackbridge_client_get_uuid(fClient)) { + jack_uuid_t uuid; + + if (jackbridge_uuid_parse(uuidstr, &uuid)) + { #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE) - const CarlaString& tcp(pData->osc.getServerPathTCP()); - const CarlaString& udp(pData->osc.getServerPathUDP()); + const CarlaString& tcp(pData->osc.getServerPathTCP()); + const CarlaString& udp(pData->osc.getServerPathUDP()); - if (tcp.isNotEmpty()) - jackbridge_set_property(fClient, uuid, - "https://kx.studio/ns/carla/osc-tcp", tcp, URI_TYPE_STRING); + if (tcp.isNotEmpty()) + jackbridge_set_property(fClient, uuid, + "https://kx.studio/ns/carla/osc-tcp", tcp, URI_TYPE_STRING); - if (tcp.isNotEmpty()) - jackbridge_set_property(fClient, uuid, - "https://kx.studio/ns/carla/osc-udp", udp, URI_TYPE_STRING); + if (tcp.isNotEmpty()) + jackbridge_set_property(fClient, uuid, + "https://kx.studio/ns/carla/osc-udp", udp, URI_TYPE_STRING); #endif - } + } - jackbridge_free(uuidstr); + jackbridge_free(uuidstr); + } } if (jackbridge_activate(fClient)) @@ -1613,10 +1619,13 @@ public: const int value1, const int value2, const int value3, const float valuef, const char* const valueStr) noexcept override { - if (action == ENGINE_CALLBACK_PROJECT_LOAD_FINISHED && fTimebaseMaster) + if (action == ENGINE_CALLBACK_PROJECT_LOAD_FINISHED) { - // project finished loading, need to set bpm here, so we force an update of timebase master - transportRelocate(pData->timeInfo.frame); + if (fTimebaseMaster) + { + // project finished loading, need to set bpm here, so we force an update of timebase master + transportRelocate(pData->timeInfo.frame); + } } CarlaEngine::callback(sendHost, sendOsc, action, pluginId, value1, value2, value3, valuef, valueStr); @@ -1631,63 +1640,67 @@ public: fPostPonedUUIDs.swapWith(uuids); } - for (int i=0; i(y2), + nullptr); } - - callback(fExternalPatchbayHost, fExternalPatchbayOsc, - ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED, - groupId, x1, y1, x2, static_cast(y2), - nullptr); } - } - jackbridge_free(value); - jackbridge_free(type); + jackbridge_free(value); + jackbridge_free(type); + } } } } @@ -1812,6 +1825,8 @@ public: jackbridge_set_process_callback(client, carla_jack_process_callback_plugin, plugin); jackbridge_on_shutdown(client, carla_jack_shutdown_callback_plugin, plugin); + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); + if (char* const uuidstr = jackbridge_client_get_uuid(client)) { jack_uuid_t uuid; @@ -1819,7 +1834,7 @@ public: if (jackbridge_uuid_parse(uuidstr, &uuid)) { char strBufId[24]; - std::snprintf(strBufId, 24, "%u", plugin->getId()); + std::snprintf(strBufId, 23, "%u", plugin->getId()); strBufId[23] = '\0'; jackbridge_set_property(client, uuid, @@ -2165,6 +2180,7 @@ public: const uint groupId, const int x1, const int y1, const int x2, const int y2) override { CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false); + CARLA_SAFE_ASSERT_RETURN(! pData->loadingProject, false); if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! external) return CarlaEngine::patchbaySetGroupPos(sendHost, sendOSC, false, groupId, x1, y1, x2, y2); @@ -2178,21 +2194,27 @@ public: CARLA_SAFE_ASSERT_RETURN(groupName != nullptr && groupName[0] != '\0', false); } - jack_uuid_t uuid; + bool ok; + { - char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, groupName); - CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0', false); + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); - const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); - jackbridge_free(uuidstr); - CARLA_SAFE_ASSERT_RETURN(parsed, false); - } + jack_uuid_t uuid; + { + char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, groupName); + CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0', false); - char valueStr[STR_MAX]; - std::snprintf(valueStr, STR_MAX-1, "%i:%i:%i:%i", x1, y1, x2, y2); - valueStr[STR_MAX-1] = '\0'; + const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); + jackbridge_free(uuidstr); + CARLA_SAFE_ASSERT_RETURN(parsed, false); + } + + char valueStr[STR_MAX]; + std::snprintf(valueStr, STR_MAX-1, "%i:%i:%i:%i", x1, y1, x2, y2); + valueStr[STR_MAX-1] = '\0'; - const bool ok = jackbridge_set_property(fClient, uuid, URI_POSITION, valueStr, URI_TYPE_STRING); + ok = jackbridge_set_property(fClient, uuid, URI_POSITION, valueStr, URI_TYPE_STRING); + } callback(sendHost, sendOSC, ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED, @@ -2367,6 +2389,7 @@ public: return CarlaEngine::getPatchbayPositions(external, count); const CarlaMutexLocker cml(fUsedGroups.mutex); + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); if (const std::size_t maxCount = fUsedGroups.list.count()) { @@ -2536,22 +2559,30 @@ public: CARLA_SAFE_ASSERT(groupId != 0); } - jack_uuid_t uuid; { - char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, ppos.name); - CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0',); + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); - const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); - jackbridge_free(uuidstr); - CARLA_SAFE_ASSERT_RETURN(parsed,); - } + jack_uuid_t uuid; + { + char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, ppos.name); + CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0',); + + const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); + jackbridge_free(uuidstr); + CARLA_SAFE_ASSERT_RETURN(parsed,); + } - char valueStr[STR_MAX]; - std::snprintf(valueStr, STR_MAX-1, "%i:%i:%i:%i", ppos.x1, ppos.y1, ppos.x2, ppos.y2); - valueStr[STR_MAX-1] = '\0'; + char valueStr[STR_MAX]; + std::snprintf(valueStr, STR_MAX-1, "%i:%i:%i:%i", ppos.x1, ppos.y1, ppos.x2, ppos.y2); + valueStr[STR_MAX-1] = '\0'; - jackbridge_set_property(fClient, uuid, URI_POSITION, valueStr, URI_TYPE_STRING); + jackbridge_set_property(fClient, uuid, URI_POSITION, valueStr, URI_TYPE_STRING); + } +# if 0 + /* NOTE: This does not seem to be needed, as JACK notifies the caller about meta-data changes, + * even for the client that triggered the change. Odd.. + */ if (groupId != 0) { callback(true, true, @@ -2559,6 +2590,7 @@ public: groupId, ppos.x1, ppos.y1, ppos.x2, static_cast(ppos.y2), nullptr); } +# endif } #endif @@ -3284,6 +3316,7 @@ private: PatchbayGroupList fUsedGroups; PatchbayPortList fUsedPorts; PatchbayConnectionList fUsedConnections; + CarlaRecursiveMutex fThreadSafeMetadataMutex; CarlaMutex fPatchbayProcThreadProtectionMutex; mutable CharStringListPtr fRetConns; @@ -3296,6 +3329,8 @@ private: if (pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS) return; + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); + jack_uuid_t uuid; { char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, clientName); @@ -3320,6 +3355,9 @@ private: CARLA_SAFE_ASSERT_RETURN(std::strcmp(type, URI_TYPE_STRING) == 0,); clientBelongsToUs = fClientName == value; + + jackbridge_free(value); + jackbridge_free(type); } { @@ -3565,62 +3603,66 @@ private: 0, 0.0f, ourName); - for (LinkedList::Itenerator it = groupCallbackData.begin2(); it.valid(); it.next()) { - const GroupToIdData& group(it.getValue(groupFallback)); - - callback(sendHost, sendOSC, - ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, - group.id, - group.icon, - group.pluginId, - 0, 0.0f, - group.strVal); + const CarlaRecursiveMutexLocker crml(fThreadSafeMetadataMutex); - jack_uuid_t uuid; + for (LinkedList::Itenerator it = groupCallbackData.begin2(); it.valid(); it.next()) { - char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, group.strVal); - CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0',); - - const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); - jackbridge_free(uuidstr); - CARLA_SAFE_ASSERT_RETURN(parsed,); - } + const GroupToIdData& group(it.getValue(groupFallback)); - char* value = nullptr; - char* type = nullptr; + callback(sendHost, sendOSC, + ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, + group.id, + group.icon, + group.pluginId, + 0, 0.0f, + group.strVal); - if (jackbridge_get_property(uuid, URI_POSITION, &value, &type) - && value != nullptr - && type != nullptr - && std::strcmp(type, URI_TYPE_STRING) == 0) - { - if (char* sep1 = std::strstr(value, ":")) + jack_uuid_t uuid; { - int x1, y1 = 0, x2 = 0, y2 = 0; - *sep1++ = '\0'; - x1 = std::atoi(value); + char* const uuidstr = jackbridge_get_uuid_for_client_name(fClient, group.strVal); + CARLA_SAFE_ASSERT_RETURN(uuidstr != nullptr && uuidstr[0] != '\0',); + + const bool parsed = jackbridge_uuid_parse(uuidstr, &uuid); + jackbridge_free(uuidstr); + CARLA_SAFE_ASSERT_RETURN(parsed,); + } + + char* value = nullptr; + char* type = nullptr; - if (char* sep2 = std::strstr(sep1, ":")) + if (jackbridge_get_property(uuid, URI_POSITION, &value, &type) + && value != nullptr + && type != nullptr + && std::strcmp(type, URI_TYPE_STRING) == 0) + { + if (char* sep1 = std::strstr(value, ":")) { - *sep2++ = '\0'; - y1 = std::atoi(sep1); + int x1, y1 = 0, x2 = 0, y2 = 0; + *sep1++ = '\0'; + x1 = std::atoi(value); - if (char* sep3 = std::strstr(sep2, ":")) + if (char* sep2 = std::strstr(sep1, ":")) { - *sep3++ = '\0'; - x2 = std::atoi(sep2); - y2 = std::atoi(sep3); + *sep2++ = '\0'; + y1 = std::atoi(sep1); + + if (char* sep3 = std::strstr(sep2, ":")) + { + *sep3++ = '\0'; + x2 = std::atoi(sep2); + y2 = std::atoi(sep3); + } } - } - jackbridge_free(value); - jackbridge_free(type); + jackbridge_free(value); + jackbridge_free(type); - callback(sendHost, sendOSC, - ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED, - group.id, x1, y1, x2, static_cast(y2), - nullptr); + callback(sendHost, sendOSC, + ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED, + group.id, x1, y1, x2, static_cast(y2), + nullptr); + } } } } diff --git a/source/frontend/patchcanvas/canvasbox.py b/source/frontend/patchcanvas/canvasbox.py index 654c141fc..5a7acadf3 100644 --- a/source/frontend/patchcanvas/canvasbox.py +++ b/source/frontend/patchcanvas/canvasbox.py @@ -226,7 +226,7 @@ class CanvasBox(QGraphicsObject): self.updatePositions() def setShadowOpacity(self, opacity): - if self.shadow: + if self.shadow is not None: self.shadow.setOpacity(opacity) def addPortFromGroup(self, port_id, port_mode, port_type, port_name, is_alternate): @@ -234,7 +234,9 @@ class CanvasBox(QGraphicsObject): if options.auto_hide_groups: if options.eyecandy == EYECANDY_FULL: CanvasItemFX(self, True, False) + self.blockSignals(True) self.setVisible(True) + self.blockSignals(False) new_widget = CanvasPort(self.m_group_id, port_id, port_name, port_mode, port_type, is_alternate, self) @@ -266,7 +268,9 @@ class CanvasBox(QGraphicsObject): if options.eyecandy == EYECANDY_FULL: CanvasItemFX(self, False, False) else: + self.blockSignals(True) self.setVisible(False) + self.blockSignals(False) def addLineFromGroup(self, line, connection_id): new_cbline = cb_line_t(line, connection_id) diff --git a/source/frontend/patchcanvas/patchcanvas.py b/source/frontend/patchcanvas/patchcanvas.py index f971dad98..98ff77d11 100644 --- a/source/frontend/patchcanvas/patchcanvas.py +++ b/source/frontend/patchcanvas/patchcanvas.py @@ -329,6 +329,7 @@ def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): group_box = CanvasBox(group_id, group_name, icon) group_box.positionChanged.connect(canvas.qobject.boxPositionChanged) + group_box.blockSignals(True) group_dict = group_dict_t() group_dict.group_id = group_id @@ -343,29 +344,26 @@ def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): if split == SPLIT_YES: group_box.setSplit(True, PORT_MODE_OUTPUT) - group_box.blockSignals(True) if features.handle_group_pos: group_box.setPos(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False))) elif old_matching_group is not None: group_box.setPos(old_matching_group[1]) else: group_box.setPos(CanvasGetNewGroupPos(False)) - group_box.blockSignals(False) group_sbox = CanvasBox(group_id, group_name, icon) - group_sbox.setSplit(True, PORT_MODE_INPUT) group_sbox.positionChanged.connect(canvas.qobject.sboxPositionChanged) + group_sbox.blockSignals(True) + group_sbox.setSplit(True, PORT_MODE_INPUT) group_dict.widgets[1] = group_sbox - group_sbox.blockSignals(True) if features.handle_group_pos: group_sbox.setPos(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True))) elif old_matching_group is not None and old_matching_group[0]: group_sbox.setPos(old_matching_group[2]) else: group_sbox.setPos(CanvasGetNewGroupPos(True)) - group_sbox.blockSignals(False) canvas.last_z_value += 1 group_sbox.setZValue(canvas.last_z_value) @@ -374,6 +372,7 @@ def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): CanvasItemFX(group_sbox, True, False) group_sbox.checkItemPos() + group_sbox.blockSignals(False) else: group_box.setSplit(False) @@ -387,11 +386,12 @@ def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM) group_box.setPos(CanvasGetNewGroupPos(horizontal)) - group_box.checkItemPos() - canvas.last_z_value += 1 group_box.setZValue(canvas.last_z_value) + group_box.checkItemPos() + group_box.blockSignals(False) + canvas.group_list.append(group_dict) if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups: