| @@ -0,0 +1,24 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <vector> | |||
| namespace rack { | |||
| namespace system { | |||
| std::vector<std::string> listEntries(std::string path); | |||
| bool isFile(std::string path); | |||
| bool isDirectory(std::string path); | |||
| void copyFile(std::string srcPath, std::string destPath); | |||
| void createDirectory(std::string path); | |||
| /** Opens a URL, also happens to work with PDFs and folders. | |||
| Shell injection is possible, so make sure the URL is trusted or hard coded. | |||
| May block, so open in a new thread. | |||
| */ | |||
| void openBrowser(std::string url); | |||
| } // namespace system | |||
| } // namespace rack | |||
| @@ -17,6 +17,7 @@ | |||
| #include "math.hpp" | |||
| #include "string.hpp" | |||
| #include "logger.hpp" | |||
| #include "system.hpp" | |||
| namespace rack { | |||
| @@ -81,62 +82,5 @@ float randomNormal(); | |||
| DEPRECATED inline float randomf() {return randomUniform();} | |||
| //////////////////// | |||
| // Operating-system specific utilities | |||
| // system.cpp | |||
| //////////////////// | |||
| std::vector<std::string> systemListEntries(std::string path); | |||
| bool systemIsFile(std::string path); | |||
| bool systemIsDirectory(std::string path); | |||
| void systemCopy(std::string srcPath, std::string destPath); | |||
| void systemCreateDirectory(std::string path); | |||
| /** Opens a URL, also happens to work with PDFs and folders. | |||
| Shell injection is possible, so make sure the URL is trusted or hard coded. | |||
| May block, so open in a new thread. | |||
| */ | |||
| void systemOpenBrowser(std::string url); | |||
| //////////////////// | |||
| // Debug logger | |||
| // logger.cpp | |||
| //////////////////// | |||
| //////////////////// | |||
| // Thread functions | |||
| //////////////////// | |||
| /** Threads which obtain a VIPLock will cause wait() to block for other less important threads. | |||
| This does not provide the VIPs with an exclusive lock. That should be left up to another mutex shared between the less important thread. | |||
| */ | |||
| struct VIPMutex { | |||
| int count = 0; | |||
| std::condition_variable cv; | |||
| std::mutex countMutex; | |||
| /** Blocks until there are no remaining VIPLocks */ | |||
| void wait() { | |||
| std::unique_lock<std::mutex> lock(countMutex); | |||
| while (count > 0) | |||
| cv.wait(lock); | |||
| } | |||
| }; | |||
| struct VIPLock { | |||
| VIPMutex &m; | |||
| VIPLock(VIPMutex &m) : m(m) { | |||
| std::unique_lock<std::mutex> lock(m.countMutex); | |||
| m.count++; | |||
| } | |||
| ~VIPLock() { | |||
| std::unique_lock<std::mutex> lock(m.countMutex); | |||
| m.count--; | |||
| lock.unlock(); | |||
| m.cv.notify_all(); | |||
| } | |||
| }; | |||
| } // namespace rack | |||
| @@ -227,7 +227,7 @@ void ModuleWidget::save(std::string filename) { | |||
| void ModuleWidget::loadDialog() { | |||
| std::string dir = asset::local("presets"); | |||
| systemCreateDirectory(dir); | |||
| system::createDirectory(dir); | |||
| osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | |||
| char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | |||
| @@ -240,7 +240,7 @@ void ModuleWidget::loadDialog() { | |||
| void ModuleWidget::saveDialog() { | |||
| std::string dir = asset::local("presets"); | |||
| systemCreateDirectory(dir); | |||
| system::createDirectory(dir); | |||
| osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | |||
| char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters); | |||
| @@ -11,7 +11,7 @@ namespace rack { | |||
| struct RegisterButton : Button { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t([&]() { | |||
| systemOpenBrowser("https://vcvrack.com/"); | |||
| system::openBrowser("https://vcvrack.com/"); | |||
| }); | |||
| t.detach(); | |||
| } | |||
| @@ -39,7 +39,7 @@ struct StatusLabel : Label { | |||
| struct ManageButton : Button { | |||
| void onAction(EventAction &e) override { | |||
| std::thread t([&]() { | |||
| systemOpenBrowser("https://vcvrack.com/plugins.html"); | |||
| system::openBrowser("https://vcvrack.com/plugins.html"); | |||
| }); | |||
| t.detach(); | |||
| } | |||
| @@ -45,7 +45,7 @@ void RackScene::step() { | |||
| if (!gLatestVersion.empty()) { | |||
| std::string versionMessage = string::stringf("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", gLatestVersion.c_str(), gApplicationVersion.c_str()); | |||
| if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { | |||
| std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | |||
| std::thread t(system::openBrowser, "https://vcvrack.com/"); | |||
| t.detach(); | |||
| windowClose(); | |||
| } | |||
| @@ -72,7 +72,7 @@ void RackWidget::loadDialog() { | |||
| std::string dir; | |||
| if (lastPath.empty()) { | |||
| dir = asset::local("patches"); | |||
| systemCreateDirectory(dir); | |||
| system::createDirectory(dir); | |||
| } | |||
| else { | |||
| dir = string::directory(lastPath); | |||
| @@ -101,7 +101,7 @@ void RackWidget::saveAsDialog() { | |||
| std::string filename; | |||
| if (lastPath.empty()) { | |||
| dir = asset::local("patches"); | |||
| systemCreateDirectory(dir); | |||
| system::createDirectory(dir); | |||
| } | |||
| else { | |||
| dir = string::directory(lastPath); | |||
| @@ -92,8 +92,8 @@ void init(bool devMode) { | |||
| } | |||
| } | |||
| systemCreateDirectory(globalDir); | |||
| systemCreateDirectory(localDir); | |||
| system::createDirectory(globalDir); | |||
| system::createDirectory(localDir); | |||
| } | |||
| @@ -27,6 +27,38 @@ static float sampleRateRequested = sampleRate; | |||
| static Module *resetModule = NULL; | |||
| static Module *randomizeModule = NULL; | |||
| /** Threads which obtain a VIPLock will cause wait() to block for other less important threads. | |||
| This does not provide the VIPs with an exclusive lock. That should be left up to another mutex shared between the less important thread. | |||
| */ | |||
| struct VIPMutex { | |||
| int count = 0; | |||
| std::condition_variable cv; | |||
| std::mutex countMutex; | |||
| /** Blocks until there are no remaining VIPLocks */ | |||
| void wait() { | |||
| std::unique_lock<std::mutex> lock(countMutex); | |||
| while (count > 0) | |||
| cv.wait(lock); | |||
| } | |||
| }; | |||
| struct VIPLock { | |||
| VIPMutex &m; | |||
| VIPLock(VIPMutex &m) : m(m) { | |||
| std::unique_lock<std::mutex> lock(m.countMutex); | |||
| m.count++; | |||
| } | |||
| ~VIPLock() { | |||
| std::unique_lock<std::mutex> lock(m.countMutex); | |||
| m.count--; | |||
| lock.unlock(); | |||
| m.cv.notify_all(); | |||
| } | |||
| }; | |||
| static std::mutex mutex; | |||
| static std::thread thread; | |||
| static VIPMutex vipMutex; | |||
| @@ -69,7 +69,7 @@ static bool loadPlugin(std::string path) { | |||
| #endif | |||
| // Check file existence | |||
| if (!systemIsFile(libraryFilename)) { | |||
| if (!system::isFile(libraryFilename)) { | |||
| warn("Plugin file %s does not exist", libraryFilename.c_str()); | |||
| return false; | |||
| } | |||
| @@ -212,8 +212,8 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||
| static void loadPlugins(std::string path) { | |||
| std::string message; | |||
| for (std::string pluginPath : systemListEntries(path)) { | |||
| if (!systemIsDirectory(pluginPath)) | |||
| for (std::string pluginPath : system::listEntries(path)) { | |||
| if (!system::isDirectory(pluginPath)) | |||
| continue; | |||
| if (!loadPlugin(pluginPath)) { | |||
| message += string::stringf("Could not load plugin %s\n", pluginPath.c_str()); | |||
| @@ -297,7 +297,7 @@ static int extractZip(const char *filename, const char *path) { | |||
| static void extractPackages(std::string path) { | |||
| std::string message; | |||
| for (std::string packagePath : systemListEntries(path)) { | |||
| for (std::string packagePath : system::listEntries(path)) { | |||
| if (string::extension(packagePath) != "zip") | |||
| continue; | |||
| info("Extracting package %s", packagePath.c_str()); | |||
| @@ -339,8 +339,8 @@ void pluginInit(bool devMode) { | |||
| std::string fundamentalSrc = asset::global("Fundamental.zip"); | |||
| std::string fundamentalDest = asset::local("plugins/Fundamental.zip"); | |||
| std::string fundamentalDir = asset::local("plugins/Fundamental"); | |||
| if (systemIsFile(fundamentalSrc) && !systemIsFile(fundamentalDest) && !systemIsDirectory(fundamentalDir)) { | |||
| systemCopy(fundamentalSrc, fundamentalDest); | |||
| if (system::isFile(fundamentalSrc) && !system::isFile(fundamentalDest) && !system::isDirectory(fundamentalDir)) { | |||
| system::copyFile(fundamentalSrc, fundamentalDest); | |||
| } | |||
| } | |||
| @@ -1,5 +1,3 @@ | |||
| #include "util/common.hpp" | |||
| #include <dirent.h> | |||
| #include <sys/stat.h> | |||
| @@ -8,11 +6,15 @@ | |||
| #include <shellapi.h> | |||
| #endif | |||
| #include "system.hpp" | |||
| #include "util/common.hpp" | |||
| namespace rack { | |||
| namespace system { | |||
| std::vector<std::string> systemListEntries(std::string path) { | |||
| std::vector<std::string> listEntries(std::string path) { | |||
| std::vector<std::string> filenames; | |||
| DIR *dir = opendir(path.c_str()); | |||
| if (dir) { | |||
| @@ -28,34 +30,31 @@ std::vector<std::string> systemListEntries(std::string path) { | |||
| return filenames; | |||
| } | |||
| bool systemExists(std::string path) { | |||
| struct stat statbuf; | |||
| return (stat(path.c_str(), &statbuf) == 0); | |||
| } | |||
| bool systemIsFile(std::string path) { | |||
| bool isFile(std::string path) { | |||
| struct stat statbuf; | |||
| if (stat(path.c_str(), &statbuf)) | |||
| return false; | |||
| return S_ISREG(statbuf.st_mode); | |||
| } | |||
| bool systemIsDirectory(std::string path) { | |||
| bool isDirectory(std::string path) { | |||
| struct stat statbuf; | |||
| if (stat(path.c_str(), &statbuf)) | |||
| return false; | |||
| return S_ISDIR(statbuf.st_mode); | |||
| } | |||
| void systemCopy(std::string srcPath, std::string destPath) { | |||
| void copyFile(std::string srcPath, std::string destPath) { | |||
| // Open files | |||
| FILE *source = fopen(srcPath.c_str(), "rb"); | |||
| if (!source) return; | |||
| if (!source) | |||
| return; | |||
| defer({ | |||
| fclose(source); | |||
| }); | |||
| FILE *dest = fopen(destPath.c_str(), "wb"); | |||
| if (!dest) return; | |||
| if (!dest) | |||
| return; | |||
| defer({ | |||
| fclose(dest); | |||
| }); | |||
| @@ -72,7 +71,7 @@ void systemCopy(std::string srcPath, std::string destPath) { | |||
| } | |||
| } | |||
| void systemCreateDirectory(std::string path) { | |||
| void createDirectory(std::string path) { | |||
| #if ARCH_WIN | |||
| CreateDirectory(path.c_str(), NULL); | |||
| #else | |||
| @@ -80,14 +79,14 @@ void systemCreateDirectory(std::string path) { | |||
| #endif | |||
| } | |||
| void systemOpenBrowser(std::string url) { | |||
| void openBrowser(std::string url) { | |||
| #if ARCH_LIN | |||
| std::string command = "xdg-open " + url; | |||
| (void) system(command.c_str()); | |||
| (void) std::system(command.c_str()); | |||
| #endif | |||
| #if ARCH_MAC | |||
| std::string command = "open " + url; | |||
| system(command.c_str()); | |||
| std::system(command.c_str()); | |||
| #endif | |||
| #if ARCH_WIN | |||
| ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); | |||
| @@ -95,4 +94,5 @@ void systemOpenBrowser(std::string url) { | |||
| } | |||
| } // namespace system | |||
| } // namespace rack | |||