Clean water File(const String&) usage
[carla.git] / source / backend / utils / PluginDiscovery.cpp
blob2ba24f5974f7c8d13766fc88857282c2fc411f40
1 // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
2 // SPDX-License-Identifier: GPL-2.0-or-later
4 #include "CarlaUtils.h"
6 #include "CarlaBackendUtils.hpp"
7 #include "CarlaBinaryUtils.hpp"
8 #include "CarlaJuceUtils.hpp"
9 #include "CarlaPipeUtils.hpp"
10 #include "CarlaSha1Utils.hpp"
11 #include "CarlaTimeUtils.hpp"
13 #include "water/files/File.h"
14 #include "water/files/FileInputStream.h"
15 #include "water/threads/ChildProcess.h"
16 #include "water/text/StringArray.h"
18 namespace CB = CARLA_BACKEND_NAMESPACE;
20 // --------------------------------------------------------------------------------------------------------------------
22 #ifndef CARLA_OS_WIN
23 static water::String findWinePrefix(const water::String filename, const int recursionLimit = 10)
25 if (recursionLimit == 0 || filename.length() < 5 || ! filename.contains("/"))
26 return "";
28 const water::String path(filename.upToLastOccurrenceOf("/", false, false));
30 if (water::File(water::String(path + "/dosdevices").toRawUTF8()).isDirectory())
31 return path;
33 return findWinePrefix(path, recursionLimit-1);
35 #endif
37 // --------------------------------------------------------------------------------------------------------------------
39 static const char* const gPluginsDiscoveryNullCharPtr = "";
41 _CarlaPluginDiscoveryMetadata::_CarlaPluginDiscoveryMetadata() noexcept
42 : name(gPluginsDiscoveryNullCharPtr),
43 maker(gPluginsDiscoveryNullCharPtr),
44 category(CB::PLUGIN_CATEGORY_NONE),
45 hints(0x0) {}
47 _CarlaPluginDiscoveryIO::_CarlaPluginDiscoveryIO() noexcept
48 : audioIns(0),
49 audioOuts(0),
50 cvIns(0),
51 cvOuts(0),
52 midiIns(0),
53 midiOuts(0),
54 parameterIns(0),
55 parameterOuts(0) {}
57 _CarlaPluginDiscoveryInfo::_CarlaPluginDiscoveryInfo() noexcept
58 : btype(CB::BINARY_NONE),
59 ptype(CB::PLUGIN_NONE),
60 filename(gPluginsDiscoveryNullCharPtr),
61 label(gPluginsDiscoveryNullCharPtr),
62 uniqueId(0),
63 metadata() {}
65 // --------------------------------------------------------------------------------------------------------------------
67 struct CarlaPluginDiscoveryOptions {
68 #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
69 struct {
70 bool autoPrefix;
71 CarlaString executable;
72 CarlaString fallbackPrefix;
73 } wine;
74 #endif
76 static CarlaPluginDiscoveryOptions& getInstance() noexcept
78 static CarlaPluginDiscoveryOptions instance;
79 return instance;
83 // --------------------------------------------------------------------------------------------------------------------
85 class CarlaPluginDiscovery : private CarlaPipeServer
87 public:
88 CarlaPluginDiscovery(const char* const discoveryTool,
89 const BinaryType btype,
90 const PluginType ptype,
91 const std::vector<water::File>&& binaries,
92 const CarlaPluginDiscoveryCallback discoveryCb,
93 const CarlaPluginCheckCacheCallback checkCacheCb,
94 void* const callbackPtr)
95 : fBinaryType(btype),
96 fPluginType(ptype),
97 fDiscoveryCallback(discoveryCb),
98 fCheckCacheCallback(checkCacheCb),
99 fCallbackPtr(callbackPtr),
100 fPluginPath(nullptr),
101 fPluginsFoundInBinary(false),
102 fBinaryIndex(0),
103 fBinaryCount(static_cast<uint>(binaries.size())),
104 fBinaries(binaries),
105 fDiscoveryTool(discoveryTool),
106 fLastMessageTime(0),
107 fNextLabel(nullptr),
108 fNextMaker(nullptr),
109 fNextName(nullptr)
111 start();
114 CarlaPluginDiscovery(const char* const discoveryTool,
115 const BinaryType btype,
116 const PluginType ptype,
117 const CarlaPluginDiscoveryCallback discoveryCb,
118 const CarlaPluginCheckCacheCallback checkCacheCb,
119 void* const callbackPtr,
120 const char* const pluginPath = nullptr)
121 : fBinaryType(btype),
122 fPluginType(ptype),
123 fDiscoveryCallback(discoveryCb),
124 fCheckCacheCallback(checkCacheCb),
125 fCallbackPtr(callbackPtr),
126 fPluginPath(pluginPath != nullptr ? carla_strdup_safe(pluginPath) : nullptr),
127 fPluginsFoundInBinary(false),
128 fBinaryIndex(0),
129 fBinaryCount(1),
130 fDiscoveryTool(discoveryTool),
131 fLastMessageTime(0),
132 fNextLabel(nullptr),
133 fNextMaker(nullptr),
134 fNextName(nullptr)
136 start();
139 ~CarlaPluginDiscovery()
141 stopPipeServer(5000);
142 std::free(fNextLabel);
143 std::free(fNextMaker);
144 std::free(fNextName);
145 delete[] fPluginPath;
148 bool idle()
150 if (isPipeRunning())
152 idlePipe();
154 // automatically skip a plugin if 30s passes without a reply
155 const uint32_t timeNow = carla_gettime_ms();
157 if (timeNow - fLastMessageTime < 30000)
158 return true;
160 carla_stdout("Plugin took too long to respond, skipping...");
161 stopPipeServer(1000);
164 // report binary as having no plugins
165 if (fCheckCacheCallback != nullptr && !fPluginsFoundInBinary && !fBinaries.empty())
167 const water::File file(fBinaries[fBinaryIndex]);
168 const water::String filename(file.getFullPathName());
170 makeHash(file, filename);
172 if (! fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
173 fDiscoveryCallback(fCallbackPtr, nullptr, fNextSha1Sum);
176 if (++fBinaryIndex == fBinaryCount)
177 return false;
179 start();
180 return true;
183 void skip()
185 if (isPipeRunning())
186 stopPipeServer(1000);
189 protected:
190 bool msgReceived(const char* const msg) noexcept
192 fLastMessageTime = carla_gettime_ms();
194 if (std::strcmp(msg, "warning") == 0 || std::strcmp(msg, "error") == 0)
196 const char* text = nullptr;
197 readNextLineAsString(text, false);
198 carla_stdout("discovery: %s", text);
199 return true;
202 if (std::strcmp(msg, "init") == 0)
204 const char* _;
205 readNextLineAsString(_, false);
206 new (&fNextInfo) _CarlaPluginDiscoveryInfo();
207 return true;
210 if (std::strcmp(msg, "end") == 0)
212 const char* _;
213 readNextLineAsString(_, false);
215 if (fNextInfo.label == nullptr)
216 fNextInfo.label = gPluginsDiscoveryNullCharPtr;
218 if (fNextInfo.metadata.maker == nullptr)
219 fNextInfo.metadata.maker = gPluginsDiscoveryNullCharPtr;
221 if (fNextInfo.metadata.name == nullptr)
222 fNextInfo.metadata.name = gPluginsDiscoveryNullCharPtr;
224 if (fBinaries.empty())
226 char* filename = nullptr;
228 if (fPluginType == CB::PLUGIN_LV2)
230 do {
231 const char* const slash = std::strchr(fNextLabel, CARLA_OS_SEP);
232 CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
233 filename = strdup(fNextLabel);
234 filename[slash - fNextLabel] = '\0';
235 fNextInfo.filename = filename;
236 fNextInfo.label = slash + 1;
237 } while (false);
240 fNextInfo.ptype = fPluginType;
241 fDiscoveryCallback(fCallbackPtr, &fNextInfo, nullptr);
243 std::free(filename);
245 else
247 CARLA_SAFE_ASSERT(fNextSha1Sum.isNotEmpty());
248 const water::String filename(fBinaries[fBinaryIndex].getFullPathName());
249 fNextInfo.filename = filename.toRawUTF8();
250 fNextInfo.ptype = fPluginType;
251 fPluginsFoundInBinary = true;
252 carla_stdout("Found %s from %s", fNextInfo.metadata.name, fNextInfo.filename);
253 fDiscoveryCallback(fCallbackPtr, &fNextInfo, fNextSha1Sum);
256 std::free(fNextLabel);
257 fNextLabel = nullptr;
259 std::free(fNextMaker);
260 fNextMaker = nullptr;
262 std::free(fNextName);
263 fNextName = nullptr;
265 return true;
268 if (std::strcmp(msg, "build") == 0)
270 uint8_t btype = 0;
271 readNextLineAsByte(btype);
272 fNextInfo.btype = static_cast<BinaryType>(btype);
273 return true;
276 if (std::strcmp(msg, "hints") == 0)
278 readNextLineAsUInt(fNextInfo.metadata.hints);
279 return true;
282 if (std::strcmp(msg, "category") == 0)
284 const char* category = nullptr;
285 readNextLineAsString(category, false);
286 fNextInfo.metadata.category = CB::getPluginCategoryFromString(category);
287 return true;
290 if (std::strcmp(msg, "name") == 0)
292 fNextInfo.metadata.name = fNextName = readNextLineAsString();
293 return true;
296 if (std::strcmp(msg, "label") == 0)
298 fNextInfo.label = fNextLabel = readNextLineAsString();
299 return true;
302 if (std::strcmp(msg, "maker") == 0)
304 fNextInfo.metadata.maker = fNextMaker = readNextLineAsString();
305 return true;
308 if (std::strcmp(msg, "uniqueId") == 0)
310 readNextLineAsULong(fNextInfo.uniqueId);
311 return true;
314 if (std::strcmp(msg, "audio.ins") == 0)
316 readNextLineAsUInt(fNextInfo.io.audioIns);
317 return true;
320 if (std::strcmp(msg, "audio.outs") == 0)
322 readNextLineAsUInt(fNextInfo.io.audioOuts);
323 return true;
326 if (std::strcmp(msg, "cv.ins") == 0)
328 readNextLineAsUInt(fNextInfo.io.cvIns);
329 return true;
332 if (std::strcmp(msg, "cv.outs") == 0)
334 readNextLineAsUInt(fNextInfo.io.cvOuts);
335 return true;
338 if (std::strcmp(msg, "midi.ins") == 0)
340 readNextLineAsUInt(fNextInfo.io.midiIns);
341 return true;
344 if (std::strcmp(msg, "midi.outs") == 0)
346 readNextLineAsUInt(fNextInfo.io.midiOuts);
347 return true;
350 if (std::strcmp(msg, "parameters.ins") == 0)
352 readNextLineAsUInt(fNextInfo.io.parameterIns);
353 return true;
356 if (std::strcmp(msg, "parameters.outs") == 0)
358 readNextLineAsUInt(fNextInfo.io.parameterOuts);
359 return true;
362 if (std::strcmp(msg, "exiting") == 0)
364 stopPipeServer(1000);
365 return true;
368 carla_stdout("discovery: unknown message '%s' received", msg);
369 return true;
372 private:
373 const BinaryType fBinaryType;
374 const PluginType fPluginType;
375 const CarlaPluginDiscoveryCallback fDiscoveryCallback;
376 const CarlaPluginCheckCacheCallback fCheckCacheCallback;
377 void* const fCallbackPtr;
378 const char* fPluginPath;
380 bool fPluginsFoundInBinary;
381 uint fBinaryIndex;
382 const uint fBinaryCount;
383 const std::vector<water::File> fBinaries;
384 const CarlaString fDiscoveryTool;
386 uint32_t fLastMessageTime;
388 CarlaPluginDiscoveryInfo fNextInfo;
389 CarlaString fNextSha1Sum;
390 char* fNextLabel;
391 char* fNextMaker;
392 char* fNextName;
394 void start()
396 using water::File;
397 using water::String;
399 fLastMessageTime = carla_gettime_ms();
400 fPluginsFoundInBinary = false;
401 fNextSha1Sum.clear();
403 #ifndef CARLA_OS_WIN
404 const CarlaPluginDiscoveryOptions& options(CarlaPluginDiscoveryOptions::getInstance());
406 String helperTool;
408 switch (fBinaryType)
410 case CB::BINARY_WIN32:
411 if (options.wine.executable.isNotEmpty())
412 helperTool = options.wine.executable.buffer();
413 else
414 helperTool = "wine";
415 break;
417 case CB::BINARY_WIN64:
418 if (options.wine.executable.isNotEmpty())
420 helperTool = options.wine.executable.buffer();
422 if (helperTool.isNotEmpty() && helperTool[0] == CARLA_OS_SEP && File(String(helperTool + "64").toRawUTF8()).existsAsFile())
423 helperTool += "64";
425 else
427 helperTool = "wine";
429 break;
431 default:
432 break;
435 String winePrefix;
437 if (options.wine.autoPrefix && !fBinaries.empty())
439 const File file(fBinaries[fBinaryIndex]);
440 const String filename(file.getFullPathName());
442 winePrefix = findWinePrefix(filename);
445 if (winePrefix.isEmpty())
447 const char* const envWinePrefix = std::getenv("WINEPREFIX");
449 if (envWinePrefix != nullptr && envWinePrefix[0] != '\0')
450 winePrefix = envWinePrefix;
451 else if (options.wine.fallbackPrefix.isNotEmpty())
452 winePrefix = options.wine.fallbackPrefix.buffer();
453 else
454 winePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine";
457 const CarlaScopedEnvVar sev1("WINEDEBUG", "-all");
458 const CarlaScopedEnvVar sev2("WINEPREFIX", winePrefix.toRawUTF8());
459 #endif
461 const CarlaScopedEnvVar sev3("CARLA_DISCOVERY_NO_PROCESSING_CHECKS", "1");
463 if (fBinaries.empty())
465 if (fBinaryType == CB::BINARY_NATIVE)
467 switch (fPluginType)
469 default:
470 break;
471 case CB::PLUGIN_INTERNAL:
472 case CB::PLUGIN_LV2:
473 case CB::PLUGIN_JSFX:
474 case CB::PLUGIN_SFZ:
475 if (const uint count = carla_get_cached_plugin_count(fPluginType, fPluginPath))
477 for (uint i=0; i<count; ++i)
479 const CarlaCachedPluginInfo* const pinfo = carla_get_cached_plugin_info(fPluginType, i);
481 if (pinfo == nullptr || !pinfo->valid)
482 continue;
484 char* filename = nullptr;
485 CarlaPluginDiscoveryInfo info = {};
486 info.btype = CB::BINARY_NATIVE;
487 info.ptype = fPluginType;
488 info.metadata.name = pinfo->name;
489 info.metadata.maker = pinfo->maker;
490 info.metadata.category = pinfo->category;
491 info.metadata.hints = pinfo->hints;
492 info.io.audioIns = pinfo->audioIns;
493 info.io.audioOuts = pinfo->audioOuts;
494 info.io.cvIns = pinfo->cvIns;
495 info.io.cvOuts = pinfo->cvOuts;
496 info.io.midiIns = pinfo->midiIns;
497 info.io.midiOuts = pinfo->midiOuts;
498 info.io.parameterIns = pinfo->parameterIns;
499 info.io.parameterOuts = pinfo->parameterOuts;
501 if (fPluginType == CB::PLUGIN_LV2)
503 const char* const slash = std::strchr(pinfo->label, CARLA_OS_SEP);
504 CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
505 filename = strdup(pinfo->label);
506 filename[slash - pinfo->label] = '\0';
507 info.filename = filename;
508 info.label = slash + 1;
510 else
512 info.filename = gPluginsDiscoveryNullCharPtr;
513 info.label = pinfo->label;
516 fDiscoveryCallback(fCallbackPtr, &info, nullptr);
518 std::free(filename);
521 return;
525 #ifndef CARLA_OS_WIN
526 if (helperTool.isNotEmpty())
527 startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all", -1, 2000);
528 else
529 #endif
530 startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all", -1, 2000);
532 else
534 const File file(fBinaries[fBinaryIndex]);
535 const String filename(file.getFullPathName());
537 if (fCheckCacheCallback != nullptr)
539 makeHash(file, filename);
541 if (fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
543 fPluginsFoundInBinary = true;
544 carla_debug("Skipping \"%s\", using cache", filename.toRawUTF8());
545 return;
549 carla_stdout("Scanning \"%s\"...", filename.toRawUTF8());
551 #ifndef CARLA_OS_WIN
552 if (helperTool.isNotEmpty())
553 startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8(), -1, 2000);
554 else
555 #endif
556 startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8(), -1, 2000);
560 void makeHash(const water::File& file, const water::String& filename)
562 CarlaSha1 sha1;
564 /* do we want this? it is not exactly needed and makes discovery slow..
565 if (file.existsAsFile() && file.getSize() < 20*1024*1024) // dont bother hashing > 20Mb files
567 water::FileInputStream stream(file);
569 if (stream.openedOk())
571 uint8_t block[8192];
572 for (int r; r = stream.read(block, sizeof(block)), r > 0;)
573 sha1.write(block, r);
578 sha1.write(filename.toRawUTF8(), filename.length());
580 const int64_t mtime = file.getLastModificationTime();
581 sha1.write(&mtime, sizeof(mtime));
583 fNextSha1Sum = sha1.resultAsString();
586 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginDiscovery)
589 // --------------------------------------------------------------------------------------------------------------------
591 static bool findDirectories(std::vector<water::File>& files, const char* const pluginPath, const char* const wildcard)
593 CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
595 if (pluginPath[0] == '\0')
596 return true;
598 using water::File;
599 using water::String;
600 using water::StringArray;
602 const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
604 if (splitPaths.size() == 0)
605 return true;
607 for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
609 const File dir(it->toRawUTF8());
610 std::vector<File> results;
612 if (dir.findChildFiles(results, File::findDirectories|File::ignoreHiddenFiles, true, wildcard) > 0)
614 files.reserve(files.size() + results.size());
615 files.insert(files.end(), results.begin(), results.end());
619 return files.empty();
622 static bool findFiles(std::vector<water::File>& files,
623 const BinaryType btype, const char* const pluginPath, const char* const wildcard)
625 CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
627 if (pluginPath[0] == '\0')
628 return true;
630 using water::File;
631 using water::String;
632 using water::StringArray;
634 const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
636 if (splitPaths.size() == 0)
637 return true;
639 for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
641 const File dir(it->toRawUTF8());
642 std::vector<File> results;
644 if (dir.findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, wildcard) > 0)
646 files.reserve(files.size() + results.size());
648 for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
650 const File file(*cit);
652 if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
653 files.push_back(file);
658 return files.empty();
661 static bool findVST3s(std::vector<water::File>& files,
662 const BinaryType btype, const char* const pluginPath)
664 CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
666 if (pluginPath[0] == '\0')
667 return true;
669 using water::File;
670 using water::String;
671 using water::StringArray;
673 const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
675 if (splitPaths.size() == 0)
676 return true;
678 const uint flags = btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64
679 ? File::findDirectories|File::findFiles
680 : File::findDirectories;
682 for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
684 const File dir(it->toRawUTF8());
685 std::vector<File> results;
687 if (dir.findChildFiles(results, flags|File::ignoreHiddenFiles, true, "*.vst3") > 0)
689 files.reserve(files.size() + results.size());
691 for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
693 const File file(*cit);
695 if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
696 files.push_back(file);
701 return files.empty();
704 CarlaPluginDiscoveryHandle carla_plugin_discovery_start(const char* const discoveryTool,
705 const BinaryType btype,
706 const PluginType ptype,
707 const char* const pluginPath,
708 const CarlaPluginDiscoveryCallback discoveryCb,
709 const CarlaPluginCheckCacheCallback checkCacheCb,
710 void* const callbackPtr)
712 CARLA_SAFE_ASSERT_RETURN(btype != CB::BINARY_NONE, nullptr);
713 CARLA_SAFE_ASSERT_RETURN(ptype != CB::PLUGIN_NONE, nullptr);
714 CARLA_SAFE_ASSERT_RETURN(discoveryTool != nullptr && discoveryTool[0] != '\0', nullptr);
715 CARLA_SAFE_ASSERT_RETURN(discoveryCb != nullptr, nullptr);
716 carla_debug("carla_plugin_discovery_start(%s, %d:%s, %d:%s, %s, %p, %p, %p)",
717 discoveryTool, btype, BinaryType2Str(btype), ptype, PluginType2Str(ptype), pluginPath,
718 discoveryCb, checkCacheCb, callbackPtr);
720 bool directories = false;
721 const char* wildcard = nullptr;
723 switch (ptype)
725 case CB::PLUGIN_INTERNAL:
726 case CB::PLUGIN_LV2:
727 case CB::PLUGIN_SFZ:
728 case CB::PLUGIN_JSFX:
729 case CB::PLUGIN_DLS:
730 case CB::PLUGIN_GIG:
731 case CB::PLUGIN_SF2:
732 CARLA_SAFE_ASSERT_UINT_RETURN(btype == CB::BINARY_NATIVE, btype, nullptr);
733 break;
734 default:
735 break;
738 switch (ptype)
740 case CB::PLUGIN_NONE:
741 case CB::PLUGIN_JACK:
742 case CB::PLUGIN_TYPE_COUNT:
743 return nullptr;
745 case CB::PLUGIN_LV2:
746 case CB::PLUGIN_SFZ:
747 case CB::PLUGIN_JSFX:
749 const CarlaScopedEnvVar csev("CARLA_DISCOVERY_PATH", pluginPath);
750 return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr, pluginPath);
753 case CB::PLUGIN_INTERNAL:
754 return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
756 case CB::PLUGIN_LADSPA:
757 case CB::PLUGIN_DSSI:
758 #ifdef CARLA_OS_WIN
759 wildcard = "*.dll";
760 #else
761 if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
763 wildcard = "*.dll";
765 else
767 #ifdef CARLA_OS_MAC
768 wildcard = "*.dylib";
769 #else
770 wildcard = "*.so";
771 #endif
773 #endif
774 break;
776 case CB::PLUGIN_VST2:
777 #ifdef CARLA_OS_WIN
778 wildcard = "*.dll";
779 #else
780 if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
782 wildcard = "*.dll";
784 else
786 #ifdef CARLA_OS_MAC
787 directories = true;
788 wildcard = "*.vst";
789 #else
790 wildcard = "*.so";
791 #endif
793 #endif
794 break;
796 case CB::PLUGIN_VST3:
797 directories = true;
798 wildcard = "*.vst3";
799 break;
801 case CB::PLUGIN_AU:
802 directories = true;
803 wildcard = "*.component";
804 break;
806 case CB::PLUGIN_CLAP:
807 wildcard = "*.clap";
808 #ifdef CARLA_OS_MAC
809 directories = true;
810 #endif
811 break;
813 case CB::PLUGIN_DLS:
814 wildcard = "*.dls";
815 break;
816 case CB::PLUGIN_GIG:
817 wildcard = "*.gig";
818 break;
819 case CB::PLUGIN_SF2:
820 wildcard = "*.sf2";
821 break;
824 CARLA_SAFE_ASSERT_RETURN(wildcard != nullptr, nullptr);
826 std::vector<water::File> files;
828 if (ptype == CB::PLUGIN_VST3)
830 if (findVST3s(files, btype, pluginPath))
831 return nullptr;
833 else if (directories)
835 if (findDirectories(files, pluginPath, wildcard))
836 return nullptr;
838 else
840 if (findFiles(files, btype, pluginPath, wildcard))
841 return nullptr;
844 return new CarlaPluginDiscovery(discoveryTool, btype, ptype, std::move(files),
845 discoveryCb, checkCacheCb, callbackPtr);
848 bool carla_plugin_discovery_idle(const CarlaPluginDiscoveryHandle handle)
850 return static_cast<CarlaPluginDiscovery*>(handle)->idle();
853 void carla_plugin_discovery_skip(const CarlaPluginDiscoveryHandle handle)
855 static_cast<CarlaPluginDiscovery*>(handle)->skip();
858 void carla_plugin_discovery_stop(const CarlaPluginDiscoveryHandle handle)
860 delete static_cast<CarlaPluginDiscovery*>(handle);
863 void carla_plugin_discovery_set_option(const EngineOption option, const int value, const char* const valueStr)
865 switch (option)
867 #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
868 case CB::ENGINE_OPTION_WINE_EXECUTABLE:
869 if (valueStr != nullptr && valueStr[0] != '\0')
870 CarlaPluginDiscoveryOptions::getInstance().wine.executable = valueStr;
871 else
872 CarlaPluginDiscoveryOptions::getInstance().wine.executable.clear();
873 break;
874 case CB::ENGINE_OPTION_WINE_AUTO_PREFIX:
875 CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
876 CarlaPluginDiscoveryOptions::getInstance().wine.autoPrefix = value != 0;
877 break;
878 case CB::ENGINE_OPTION_WINE_FALLBACK_PREFIX:
879 if (valueStr != nullptr && valueStr[0] != '\0')
880 CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix = valueStr;
881 else
882 CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix.clear();
883 break;
884 #endif
885 default:
886 break;
890 // --------------------------------------------------------------------------------------------------------------------