From 68bd68b6f18b96d47a41ff60653a316c2001dd0b Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 19 Oct 2017 21:54:29 +0200 Subject: [PATCH] Implement automatic find of plugin wineprefix Thanks to Jan Ypma for some base code Closes #332 Closes #374 --- resources/ui/carla_settings.ui | 3 - source/backend/plugin/CarlaPluginBridge.cpp | 159 ++++++++++++-------- source/carla_database.py | 85 ++++++++--- 3 files changed, 158 insertions(+), 89 deletions(-) diff --git a/resources/ui/carla_settings.ui b/resources/ui/carla_settings.ui index 9c0d78720..53d1f1394 100644 --- a/resources/ui/carla_settings.ui +++ b/resources/ui/carla_settings.ui @@ -1478,9 +1478,6 @@ - - false - Auto-detect Wine prefix based on plugin filename diff --git a/source/backend/plugin/CarlaPluginBridge.cpp b/source/backend/plugin/CarlaPluginBridge.cpp index 5e60620e0..68b60396f 100644 --- a/source/backend/plugin/CarlaPluginBridge.cpp +++ b/source/backend/plugin/CarlaPluginBridge.cpp @@ -30,7 +30,7 @@ #include -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- using juce::ChildProcess; using juce::File; @@ -41,12 +41,27 @@ using juce::Time; CARLA_BACKEND_START_NAMESPACE -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- // Fallback data static const ExternalMidiNote kExternalMidiNoteFallback = { -1, 0, 0 }; -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- + +static String findWinePrefix(const String filename, const int recursionLimit = 10) +{ + if (recursionLimit == 0 || filename.length() < 5 || ! filename.contains("/")) + return ""; + + const String path(filename.upToLastOccurrenceOf("/", false, false)); + + if (File(path + "/dosdevices").isDirectory()) + return path; + + return findWinePrefix(path, recursionLimit-1); +} + +// --------------------------------------------------------------------------------------------------------------------- struct BridgeParamInfo { float value; @@ -63,7 +78,7 @@ struct BridgeParamInfo { CARLA_DECLARE_NON_COPY_STRUCT(BridgeParamInfo) }; -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- class CarlaPluginBridgeThread : public CarlaThread { @@ -77,12 +92,16 @@ public: fShmIds(), fProcess() {} - void setData(const char* const binary, const char* const label, const char* const shmIds) noexcept + void setData(const char* const winePrefix, + const char* const binary, + const char* const label, + const char* const shmIds) noexcept { CARLA_SAFE_ASSERT_RETURN(binary != nullptr && binary[0] != '\0',); CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',); CARLA_SAFE_ASSERT(! isThreadRunning()); + fWinePrefix = winePrefix; fBinary = binary; fShmIds = shmIds; @@ -135,41 +154,6 @@ protected: arguments.add(options.wine.executable); else arguments.add("wine"); - -#if 0 - if (options.wine.autoPrefix) - { - // TODO - } - else -#endif - if (std::getenv("WINEPREFIX") == nullptr && - options.wine.fallbackPrefix != nullptr && - options.wine.fallbackPrefix[0] != '\0') - { - carla_setenv("WINEPREFIX", options.wine.fallbackPrefix); - } - - if (options.wine.rtPrio) - { - carla_setenv("STAGING_SHARED_MEMORY", "1"); - - std::snprintf(strBuf, STR_MAX, "%i", options.wine.baseRtPrio); - carla_setenv("STAGING_RT_PRIORITY_BASE", strBuf); - carla_setenv("WINE_RT", strBuf); - - std::snprintf(strBuf, STR_MAX, "%i", options.wine.serverRtPrio); - carla_setenv("STAGING_RT_PRIORITY_SERVER", strBuf); - carla_setenv("WINE_SVR_RT", strBuf); - } - else - { - carla_unsetenv("STAGING_SHARED_MEMORY"); - carla_unsetenv("STAGING_RT_PRIORITY_BASE"); - carla_unsetenv("STAGING_RT_PRIORITY_SERVER"); - carla_unsetenv("WINE_RT"); - carla_unsetenv("WINE_SVR_RT"); - } } #endif @@ -265,7 +249,37 @@ protected: carla_setenv("ENGINE_OPTION_FRONTEND_WIN_ID", strBuf); carla_setenv("ENGINE_BRIDGE_SHM_IDS", fShmIds.toRawUTF8()); - carla_setenv("WINEDEBUG", "-all"); + +#ifndef CARLA_OS_WIN + if (fWinePrefix.isNotEmpty()) + { + carla_setenv("WINEDEBUG", "-all"); + carla_setenv("WINEPREFIX", fWinePrefix.toRawUTF8()); + + if (options.wine.rtPrio) + { + carla_setenv("STAGING_SHARED_MEMORY", "1"); + + std::snprintf(strBuf, STR_MAX, "%i", options.wine.baseRtPrio); + carla_setenv("STAGING_RT_PRIORITY_BASE", strBuf); + carla_setenv("WINE_RT", strBuf); + + std::snprintf(strBuf, STR_MAX, "%i", options.wine.serverRtPrio); + carla_setenv("STAGING_RT_PRIORITY_SERVER", strBuf); + carla_setenv("WINE_SVR_RT", strBuf); + } + else + { + carla_unsetenv("STAGING_SHARED_MEMORY"); + carla_unsetenv("STAGING_RT_PRIORITY_BASE"); + carla_unsetenv("STAGING_RT_PRIORITY_SERVER"); + carla_unsetenv("WINE_RT"); + carla_unsetenv("WINE_SVR_RT"); + } + + carla_stdout("Using WINEPREFIX '%s'", fWinePrefix.toRawUTF8()); + } +#endif carla_stdout("starting plugin bridge, command is:\n%s \"%s\" \"%s\" \"%s\" " P_INT64, fBinary.toRawUTF8(), getPluginTypeAsString(kPlugin->getType()), filename.toRawUTF8(), fLabel.toRawUTF8(), kPlugin->getUniqueId()); @@ -319,6 +333,7 @@ private: CarlaEngine* const kEngine; CarlaPlugin* const kPlugin; + String fWinePrefix; String fBinary; String fLabel; String fShmIds; @@ -328,7 +343,7 @@ private: CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginBridgeThread) }; -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- class CarlaPluginBridge : public CarlaPlugin { @@ -2027,25 +2042,17 @@ public: fShmNonRtServerControl.readCustomData(chunkFilePath, chunkFilePathSize); String realChunkFilePath(chunkFilePath); - carla_stdout("chunk save path BEFORE => %s", realChunkFilePath.toRawUTF8()); #ifndef CARLA_OS_WIN // Using Wine, fix temp dir if (fBinaryType == BINARY_WIN32 || fBinaryType == BINARY_WIN64) { - // Get WINEPREFIX - String wineDir; - if (const char* const WINEPREFIX = getenv("WINEPREFIX")) - wineDir = String(WINEPREFIX); - else - wineDir = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine"; - const StringArray driveLetterSplit(StringArray::fromTokens(realChunkFilePath, ":/", "")); + carla_stdout("chunk save path BEFORE => %s", realChunkFilePath.toRawUTF8()); - realChunkFilePath = wineDir; + realChunkFilePath = fWinePrefix; realChunkFilePath += "/drive_"; realChunkFilePath += driveLetterSplit[0].toLowerCase(); - realChunkFilePath += "/"; realChunkFilePath += driveLetterSplit[1]; realChunkFilePath = realChunkFilePath.replace("\\", "/"); @@ -2054,12 +2061,10 @@ public: #endif File chunkFile(realChunkFilePath); + CARLA_SAFE_ASSERT_BREAK(chunkFile.existsAsFile()); - if (chunkFile.existsAsFile()) - { - fInfo.chunk = carla_getChunkFromBase64String(chunkFile.loadFileAsString().toRawUTF8()); - chunkFile.deleteFile(); - } + fInfo.chunk = carla_getChunkFromBase64String(chunkFile.loadFileAsString().toRawUTF8()); + chunkFile.deleteFile(); } break; case kPluginBridgeNonRtServerSetLatency: @@ -2193,8 +2198,8 @@ public: } // --------------------------------------------------------------- - // initial values + fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientNull); fShmNonRtClientControl.writeUInt(static_cast(sizeof(BridgeRtClientData))); fShmNonRtClientControl.writeUInt(static_cast(sizeof(BridgeNonRtClientData))); @@ -2210,7 +2215,32 @@ public: fShmRtClientControl.writeOpcode(kPluginBridgeRtClientNull); fShmRtClientControl.commitWrite(); + // --------------------------------------------------------------- + // set wine prefix + + if (fBridgeBinary.contains(".exe", true)) + { + const EngineOptions& options(pData->engine->getOptions()); + + if (options.wine.autoPrefix) + fWinePrefix = findWinePrefix(pData->filename); + + if (fWinePrefix.isEmpty()) + { + const char* const envWinePrefix(std::getenv("WINEPREFIX")); + + if (envWinePrefix != nullptr && envWinePrefix[0] != '\0') + fWinePrefix = envWinePrefix; + else if (options.wine.fallbackPrefix != nullptr && options.wine.fallbackPrefix[0] != '\0') + fWinePrefix = options.wine.fallbackPrefix; + else + fWinePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine"; + } + } + + // --------------------------------------------------------------- // init bridge thread + { char shmIdsStr[6*4+1]; carla_zeroChars(shmIdsStr, 6*4+1); @@ -2220,10 +2250,13 @@ public: std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.filename.length()-6], 6); - fBridgeThread.setData(bridgeBinary, label, shmIdsStr); + fBridgeThread.setData(fWinePrefix.toRawUTF8(), bridgeBinary, label, shmIdsStr); fBridgeThread.startThread(); } + // --------------------------------------------------------------- + // wait for bridge to start + fInitiated = false; fLastPongTime = Time::currentTimeMillis(); CARLA_SAFE_ASSERT(fLastPongTime > 0); @@ -2314,6 +2347,8 @@ private: BridgeNonRtClientControl fShmNonRtClientControl; BridgeNonRtServerControl fShmNonRtServerControl; + String fWinePrefix; + struct Info { uint32_t aIns, aOuts; uint32_t cvIns, cvOuts; @@ -2381,7 +2416,7 @@ private: CARLA_BACKEND_END_NAMESPACE -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- CARLA_BACKEND_START_NAMESPACE @@ -2414,10 +2449,10 @@ CarlaPlugin* CarlaPlugin::newBridge(const Initializer& init, BinaryType btype, P CARLA_BACKEND_END_NAMESPACE -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- #ifndef BUILD_BRIDGE # include "CarlaBridgeUtils.cpp" #endif -// ------------------------------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------------------------------- diff --git a/source/carla_database.py b/source/carla_database.py index c3e1eb8a2..58e6717f8 100755 --- a/source/carla_database.py +++ b/source/carla_database.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Carla plugin database code -# Copyright (C) 2011-2016 Filipe Coelho +# Copyright (C) 2011-2017 Filipe Coelho # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -162,7 +162,18 @@ PyPluginInfo = { global gDiscoveryProcess gDiscoveryProcess = None -def runCarlaDiscovery(itype, stype, filename, tool, isWine=False): +def findWinePrefix(filename, recursionLimit = 10): + if recursionLimit == 0 or len(filename) < 5 or "/" not in filename: + return "" + + path = filename[:filename.rfind("/")] + + if os.path.isdir(path + "/dosdevices"): + return path + + return findWinePrefix(path, recursionLimit-1) + +def runCarlaDiscovery(itype, stype, filename, tool, wineSettings=None): if not os.path.exists(tool): qWarning("runCarlaDiscovery() - tool '%s' does not exist" % tool) return @@ -173,9 +184,26 @@ def runCarlaDiscovery(itype, stype, filename, tool, isWine=False): command.append("env") command.append("LANG=C") command.append("LD_PRELOAD=") - if isWine: + if wineSettings is not None: command.append("WINEDEBUG=-all") - command.append("wine") + + if wineSettings['autoPrefix']: + winePrefix = findWinePrefix(filename) + else: + winePrefix = "" + + if not winePrefix: + envWinePrefix = os.getenv("WINEPREFIX") + + if envWinePrefix: + winePrefix = envWinePrefix + elif wineSettings['fallbackPrefix']: + winePrefix = os.path.expanduser(wineSettings['fallbackPrefix']) + else: + winePrefix = os.path.expanduser("~/.wine") + + command.append("WINEPREFIX=" + winePrefix) + command.append(wineSettings['executable'] if wineSettings['executable'] else "wine") command.append(tool) command.append(stype) @@ -314,32 +342,32 @@ def checkPluginCached(desc, ptype): return plugins -def checkPluginLADSPA(filename, tool, isWine=False): - return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, isWine) +def checkPluginLADSPA(filename, tool, wineSettings=None): + return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, wineSettings) -def checkPluginDSSI(filename, tool, isWine=False): - return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, isWine) +def checkPluginDSSI(filename, tool, wineSettings=None): + return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, wineSettings) -def checkPluginLV2(filename, tool, isWine=False): - return runCarlaDiscovery(PLUGIN_LV2, "LV2", filename, tool, isWine) +def checkPluginLV2(filename, tool, wineSettings=None): + return runCarlaDiscovery(PLUGIN_LV2, "LV2", filename, tool, wineSettings) -def checkPluginVST2(filename, tool, isWine=False): - return runCarlaDiscovery(PLUGIN_VST2, "VST2", filename, tool, isWine) +def checkPluginVST2(filename, tool, wineSettings=None): + return runCarlaDiscovery(PLUGIN_VST2, "VST2", filename, tool, wineSettings) -def checkPluginVST3(filename, tool, isWine=False): - return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, isWine) +def checkPluginVST3(filename, tool, wineSettings=None): + return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) def checkPluginAU(tool): - return runCarlaDiscovery(PLUGIN_AU, "AU", ":all", tool) + return runCarlaDiscovery(None, PLUGIN_AU, "AU", ":all", tool) def checkFileGIG(filename, tool): - return runCarlaDiscovery(PLUGIN_GIG, "GIG", filename, tool) + return runCarlaDiscovery(None, PLUGIN_GIG, "GIG", filename, tool) def checkFileSF2(filename, tool): - return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) + return runCarlaDiscovery(None, PLUGIN_SF2, "SF2", filename, tool) def checkFileSFZ(filename, tool): - return runCarlaDiscovery(PLUGIN_SFZ, "SFZ", filename, tool) + return runCarlaDiscovery(None, PLUGIN_SFZ, "SFZ", filename, tool) # ---------------------------------------------------------------------------------------------------------------------- # Separate Thread for Plugin Search @@ -371,9 +399,19 @@ class SearchPluginsThread(QThread): if WINDOWS: toolNative = "carla-discovery-win64.exe" if kIs64bit else "carla-discovery-win32.exe" + self.fWineSettings = None + else: toolNative = "carla-discovery-native" + settings = QSettings("falkTX", "Carla2") + self.fWineSettings = { + 'executable' : settings.value(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE, type=str), + 'autoPrefix' : settings.value(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX, type=bool), + 'fallbackPrefix': settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX, type=str) + } + del settings + self.fToolNative = os.path.join(pathBinaries, toolNative) if not os.path.exists(self.fToolNative): @@ -713,7 +751,7 @@ class SearchPluginsThread(QThread): percent = ( float(i) / len(ladspaBinaries) ) * self.fCurPercentValue self._pluginLook((self.fLastCheckValue + percent) * 0.9, ladspa) - plugins = checkPluginLADSPA(ladspa, tool, isWine) + plugins = checkPluginLADSPA(ladspa, tool, self.fWineSettings if isWine else None) if plugins: self.fLadspaPlugins.append(plugins) @@ -746,7 +784,7 @@ class SearchPluginsThread(QThread): percent = ( float(i) / len(dssiBinaries) ) * self.fCurPercentValue self._pluginLook(self.fLastCheckValue + percent, dssi) - plugins = checkPluginDSSI(dssi, tool, isWine) + plugins = checkPluginDSSI(dssi, tool, self.fWineSettings if isWine else None) if plugins: self.fDssiPlugins.append(plugins) @@ -785,7 +823,7 @@ class SearchPluginsThread(QThread): percent = ( float(i) / len(vst2Binaries) ) * self.fCurPercentValue self._pluginLook(self.fLastCheckValue + percent, vst2) - plugins = checkPluginVST2(vst2, tool, isWine) + plugins = checkPluginVST2(vst2, tool, self.fWineSettings if isWine else None) if plugins: self.fVstPlugins.append(plugins) @@ -824,7 +862,7 @@ class SearchPluginsThread(QThread): percent = ( float(i) / len(vst3Binaries) ) * self.fCurPercentValue self._pluginLook(self.fLastCheckValue + percent, vst3) - plugins = checkPluginVST3(vst3, tool, isWine) + plugins = checkPluginVST3(vst3, tool, self.fWineSettings if isWine else None) if plugins: self.fVst3Plugins.append(plugins) @@ -928,8 +966,7 @@ class PluginRefreshW(QDialog): if False: # kdevelop likes this :) - host = CarlaHostNull() - self.host = host + self.host = host = CarlaHostNull() # -------------------------------------------------------------------------------------------------------------- # Internal stuff