@@ -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 "math.hpp" | ||||
#include "string.hpp" | #include "string.hpp" | ||||
#include "logger.hpp" | #include "logger.hpp" | ||||
#include "system.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -81,62 +82,5 @@ float randomNormal(); | |||||
DEPRECATED inline float randomf() {return randomUniform();} | 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 | } // namespace rack |
@@ -227,7 +227,7 @@ void ModuleWidget::save(std::string filename) { | |||||
void ModuleWidget::loadDialog() { | void ModuleWidget::loadDialog() { | ||||
std::string dir = asset::local("presets"); | std::string dir = asset::local("presets"); | ||||
systemCreateDirectory(dir); | |||||
system::createDirectory(dir); | |||||
osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | ||||
char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | ||||
@@ -240,7 +240,7 @@ void ModuleWidget::loadDialog() { | |||||
void ModuleWidget::saveDialog() { | void ModuleWidget::saveDialog() { | ||||
std::string dir = asset::local("presets"); | std::string dir = asset::local("presets"); | ||||
systemCreateDirectory(dir); | |||||
system::createDirectory(dir); | |||||
osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str()); | ||||
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters); | char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters); | ||||
@@ -11,7 +11,7 @@ namespace rack { | |||||
struct RegisterButton : Button { | struct RegisterButton : Button { | ||||
void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
std::thread t([&]() { | std::thread t([&]() { | ||||
systemOpenBrowser("https://vcvrack.com/"); | |||||
system::openBrowser("https://vcvrack.com/"); | |||||
}); | }); | ||||
t.detach(); | t.detach(); | ||||
} | } | ||||
@@ -39,7 +39,7 @@ struct StatusLabel : Label { | |||||
struct ManageButton : Button { | struct ManageButton : Button { | ||||
void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
std::thread t([&]() { | std::thread t([&]() { | ||||
systemOpenBrowser("https://vcvrack.com/plugins.html"); | |||||
system::openBrowser("https://vcvrack.com/plugins.html"); | |||||
}); | }); | ||||
t.detach(); | t.detach(); | ||||
} | } | ||||
@@ -45,7 +45,7 @@ void RackScene::step() { | |||||
if (!gLatestVersion.empty()) { | 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()); | 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())) { | 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(); | t.detach(); | ||||
windowClose(); | windowClose(); | ||||
} | } | ||||
@@ -72,7 +72,7 @@ void RackWidget::loadDialog() { | |||||
std::string dir; | std::string dir; | ||||
if (lastPath.empty()) { | if (lastPath.empty()) { | ||||
dir = asset::local("patches"); | dir = asset::local("patches"); | ||||
systemCreateDirectory(dir); | |||||
system::createDirectory(dir); | |||||
} | } | ||||
else { | else { | ||||
dir = string::directory(lastPath); | dir = string::directory(lastPath); | ||||
@@ -101,7 +101,7 @@ void RackWidget::saveAsDialog() { | |||||
std::string filename; | std::string filename; | ||||
if (lastPath.empty()) { | if (lastPath.empty()) { | ||||
dir = asset::local("patches"); | dir = asset::local("patches"); | ||||
systemCreateDirectory(dir); | |||||
system::createDirectory(dir); | |||||
} | } | ||||
else { | else { | ||||
dir = string::directory(lastPath); | 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 *resetModule = NULL; | ||||
static Module *randomizeModule = 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::mutex mutex; | ||||
static std::thread thread; | static std::thread thread; | ||||
static VIPMutex vipMutex; | static VIPMutex vipMutex; | ||||
@@ -69,7 +69,7 @@ static bool loadPlugin(std::string path) { | |||||
#endif | #endif | ||||
// Check file existence | // Check file existence | ||||
if (!systemIsFile(libraryFilename)) { | |||||
if (!system::isFile(libraryFilename)) { | |||||
warn("Plugin file %s does not exist", libraryFilename.c_str()); | warn("Plugin file %s does not exist", libraryFilename.c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
@@ -212,8 +212,8 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
static void loadPlugins(std::string path) { | static void loadPlugins(std::string path) { | ||||
std::string message; | std::string message; | ||||
for (std::string pluginPath : systemListEntries(path)) { | |||||
if (!systemIsDirectory(pluginPath)) | |||||
for (std::string pluginPath : system::listEntries(path)) { | |||||
if (!system::isDirectory(pluginPath)) | |||||
continue; | continue; | ||||
if (!loadPlugin(pluginPath)) { | if (!loadPlugin(pluginPath)) { | ||||
message += string::stringf("Could not load plugin %s\n", pluginPath.c_str()); | 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) { | static void extractPackages(std::string path) { | ||||
std::string message; | std::string message; | ||||
for (std::string packagePath : systemListEntries(path)) { | |||||
for (std::string packagePath : system::listEntries(path)) { | |||||
if (string::extension(packagePath) != "zip") | if (string::extension(packagePath) != "zip") | ||||
continue; | continue; | ||||
info("Extracting package %s", packagePath.c_str()); | info("Extracting package %s", packagePath.c_str()); | ||||
@@ -339,8 +339,8 @@ void pluginInit(bool devMode) { | |||||
std::string fundamentalSrc = asset::global("Fundamental.zip"); | std::string fundamentalSrc = asset::global("Fundamental.zip"); | ||||
std::string fundamentalDest = asset::local("plugins/Fundamental.zip"); | std::string fundamentalDest = asset::local("plugins/Fundamental.zip"); | ||||
std::string fundamentalDir = asset::local("plugins/Fundamental"); | 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 <dirent.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
@@ -8,11 +6,15 @@ | |||||
#include <shellapi.h> | #include <shellapi.h> | ||||
#endif | #endif | ||||
#include "system.hpp" | |||||
#include "util/common.hpp" | |||||
namespace rack { | 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; | std::vector<std::string> filenames; | ||||
DIR *dir = opendir(path.c_str()); | DIR *dir = opendir(path.c_str()); | ||||
if (dir) { | if (dir) { | ||||
@@ -28,34 +30,31 @@ std::vector<std::string> systemListEntries(std::string path) { | |||||
return filenames; | 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; | struct stat statbuf; | ||||
if (stat(path.c_str(), &statbuf)) | if (stat(path.c_str(), &statbuf)) | ||||
return false; | return false; | ||||
return S_ISREG(statbuf.st_mode); | return S_ISREG(statbuf.st_mode); | ||||
} | } | ||||
bool systemIsDirectory(std::string path) { | |||||
bool isDirectory(std::string path) { | |||||
struct stat statbuf; | struct stat statbuf; | ||||
if (stat(path.c_str(), &statbuf)) | if (stat(path.c_str(), &statbuf)) | ||||
return false; | return false; | ||||
return S_ISDIR(statbuf.st_mode); | return S_ISDIR(statbuf.st_mode); | ||||
} | } | ||||
void systemCopy(std::string srcPath, std::string destPath) { | |||||
void copyFile(std::string srcPath, std::string destPath) { | |||||
// Open files | // Open files | ||||
FILE *source = fopen(srcPath.c_str(), "rb"); | FILE *source = fopen(srcPath.c_str(), "rb"); | ||||
if (!source) return; | |||||
if (!source) | |||||
return; | |||||
defer({ | defer({ | ||||
fclose(source); | fclose(source); | ||||
}); | }); | ||||
FILE *dest = fopen(destPath.c_str(), "wb"); | FILE *dest = fopen(destPath.c_str(), "wb"); | ||||
if (!dest) return; | |||||
if (!dest) | |||||
return; | |||||
defer({ | defer({ | ||||
fclose(dest); | 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 | #if ARCH_WIN | ||||
CreateDirectory(path.c_str(), NULL); | CreateDirectory(path.c_str(), NULL); | ||||
#else | #else | ||||
@@ -80,14 +79,14 @@ void systemCreateDirectory(std::string path) { | |||||
#endif | #endif | ||||
} | } | ||||
void systemOpenBrowser(std::string url) { | |||||
void openBrowser(std::string url) { | |||||
#if ARCH_LIN | #if ARCH_LIN | ||||
std::string command = "xdg-open " + url; | std::string command = "xdg-open " + url; | ||||
(void) system(command.c_str()); | |||||
(void) std::system(command.c_str()); | |||||
#endif | #endif | ||||
#if ARCH_MAC | #if ARCH_MAC | ||||
std::string command = "open " + url; | std::string command = "open " + url; | ||||
system(command.c_str()); | |||||
std::system(command.c_str()); | |||||
#endif | #endif | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); | ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL); | ||||
@@ -95,4 +94,5 @@ void systemOpenBrowser(std::string url) { | |||||
} | } | ||||
} // namespace system | |||||
} // namespace rack | } // namespace rack |