@@ -18,8 +18,8 @@ std::string user(std::string filename); | |||
std::string plugin(Plugin *plugin, std::string filename); | |||
extern std::string gSystemDir; | |||
extern std::string gUserDir; | |||
extern std::string systemDir; | |||
extern std::string userDir; | |||
} // namespace asset | |||
@@ -11,7 +11,6 @@ namespace event { | |||
struct Scene; | |||
struct Engine; | |||
struct PluginManager; | |||
struct Window; | |||
@@ -19,7 +18,6 @@ struct Context { | |||
event::Context *event = NULL; | |||
Scene *scene = NULL; | |||
Engine *engine = NULL; | |||
PluginManager *plugin = NULL; | |||
Window *window = NULL; | |||
}; | |||
@@ -0,0 +1,33 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "plugin/Plugin.hpp" | |||
#include "plugin/Model.hpp" | |||
#include <list> | |||
namespace rack { | |||
namespace plugin { | |||
void init(bool devMode); | |||
void destroy(); | |||
void logIn(std::string email, std::string password); | |||
void logOut(); | |||
/** Returns whether a new plugin is available, and downloads it unless doing a dry run */ | |||
bool sync(bool dryRun); | |||
void cancelDownload(); | |||
bool isLoggedIn(); | |||
Plugin *getPlugin(std::string pluginSlug); | |||
Model *getModel(std::string pluginSlug, std::string modelSlug); | |||
extern std::list<Plugin*> plugins; | |||
extern std::string token; | |||
extern bool isDownloading; | |||
extern float downloadProgress; | |||
extern std::string downloadName; | |||
extern std::string loginStatus; | |||
} // namespace plugin | |||
} // namespace rack |
@@ -1,44 +0,0 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "plugin/Plugin.hpp" | |||
#include "plugin/Model.hpp" | |||
#include <list> | |||
namespace rack { | |||
struct PluginManager { | |||
std::list<Plugin*> plugins; | |||
std::string token; | |||
bool isDownloading = false; | |||
float downloadProgress = 0.f; | |||
std::string downloadName; | |||
std::string loginStatus; | |||
PluginManager(bool devMode); | |||
~PluginManager(); | |||
void logIn(std::string email, std::string password); | |||
void logOut(); | |||
/** Returns whether a new plugin is available, and downloads it unless doing a dry run */ | |||
bool sync(bool dryRun); | |||
void cancelDownload(); | |||
bool isLoggedIn(); | |||
Plugin *getPlugin(std::string pluginSlug); | |||
Model *getModel(std::string pluginSlug, std::string modelSlug); | |||
}; | |||
} // namespace rack | |||
//////////////////// | |||
// Implemented by plugin | |||
//////////////////// | |||
/** Called once to initialize and return the Plugin instance. | |||
You must implement this in your plugin | |||
*/ | |||
extern "C" | |||
void init(rack::Plugin *plugin); |
@@ -10,7 +10,7 @@ | |||
#include "app/Scene.hpp" | |||
#include "ui/List.hpp" | |||
#include "ui/TextField.hpp" | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "context.hpp" | |||
@@ -323,7 +323,7 @@ struct ModuleBrowser : OpaqueWidget { | |||
addChild(moduleScroll); | |||
// Collect authors | |||
for (Plugin *plugin : context()->plugin->plugins) { | |||
for (Plugin *plugin : plugin::plugins) { | |||
for (Model *model : plugin->models) { | |||
// Insert author | |||
if (!model->author.empty()) | |||
@@ -428,7 +428,7 @@ struct ModuleBrowser : OpaqueWidget { | |||
moduleList->addChild(item); | |||
} | |||
// Modules | |||
for (Plugin *plugin : context()->plugin->plugins) { | |||
for (Plugin *plugin : plugin::plugins) { | |||
for (Model *model : plugin->models) { | |||
if (isModelFiltered(model) && isModelMatch(model, search)) { | |||
ModelItem *item = new ModelItem; | |||
@@ -588,7 +588,7 @@ void moduleBrowserFromJson(json_t *rootJ) { | |||
continue; | |||
std::string pluginSlug = json_string_value(pluginJ); | |||
std::string modelSlug = json_string_value(modelJ); | |||
Model *model = context()->plugin->getModel(pluginSlug, modelSlug); | |||
Model *model = plugin::getModel(pluginSlug, modelSlug); | |||
if (!model) | |||
continue; | |||
sFavoriteModels.insert(model); | |||
@@ -7,7 +7,7 @@ | |||
#include "ui/TextField.hpp" | |||
#include "ui/PasswordField.hpp" | |||
#include "ui/Label.hpp" | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "context.hpp" | |||
#include "window.hpp" | |||
#include "helpers.hpp" | |||
@@ -32,7 +32,7 @@ struct LogInButton : Button { | |||
TextField *passwordField; | |||
void onAction(event::Action &e) override { | |||
std::thread t([&]() { | |||
context()->plugin->logIn(emailField->text, passwordField->text); | |||
plugin::logIn(emailField->text, passwordField->text); | |||
}); | |||
t.detach(); | |||
passwordField->text = ""; | |||
@@ -42,7 +42,7 @@ struct LogInButton : Button { | |||
struct StatusLabel : Label { | |||
void step() override { | |||
text = context()->plugin->loginStatus; | |||
text = plugin::loginStatus; | |||
} | |||
}; | |||
@@ -68,7 +68,7 @@ struct SyncButton : Button { | |||
// Check for plugin update on first step() | |||
if (!checked) { | |||
std::thread t([this]() { | |||
if (context()->plugin->sync(true)) | |||
if (plugin::sync(true)) | |||
available = true; | |||
}); | |||
t.detach(); | |||
@@ -97,7 +97,7 @@ struct SyncButton : Button { | |||
void onAction(event::Action &e) override { | |||
available = false; | |||
std::thread t([this]() { | |||
if (context()->plugin->sync(false)) | |||
if (plugin::sync(false)) | |||
completed = true; | |||
}); | |||
t.detach(); | |||
@@ -107,14 +107,14 @@ struct SyncButton : Button { | |||
struct LogOutButton : Button { | |||
void onAction(event::Action &e) override { | |||
context()->plugin->logOut(); | |||
plugin::logOut(); | |||
} | |||
}; | |||
struct DownloadQuantity : Quantity { | |||
float getValue() override { | |||
return context()->plugin->downloadProgress; | |||
return plugin::downloadProgress; | |||
} | |||
float getDisplayValue() override { | |||
@@ -124,7 +124,7 @@ struct DownloadQuantity : Quantity { | |||
int getDisplayPrecision() override {return 0;} | |||
std::string getLabel() override { | |||
return "Downloading " + context()->plugin->downloadName; | |||
return "Downloading " + plugin::downloadName; | |||
} | |||
std::string getUnit() override {return "%";} | |||
@@ -140,7 +140,7 @@ struct DownloadProgressBar : ProgressBar { | |||
struct CancelButton : Button { | |||
void onAction(event::Action &e) override { | |||
context()->plugin->cancelDownload(); | |||
plugin::cancelDownload(); | |||
} | |||
}; | |||
@@ -227,9 +227,9 @@ void PluginManagerWidget::step() { | |||
manageWidget->visible = false; | |||
downloadWidget->visible = false; | |||
if (context()->plugin->isDownloading) | |||
if (plugin::isDownloading) | |||
downloadWidget->visible = true; | |||
else if (context()->plugin->isLoggedIn()) | |||
else if (plugin::isLoggedIn()) | |||
manageWidget->visible = true; | |||
else | |||
loginWidget->visible = true; | |||
@@ -9,7 +9,7 @@ | |||
#include "asset.hpp" | |||
#include "system.hpp" | |||
#include "logger.hpp" | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "context.hpp" | |||
@@ -379,7 +379,7 @@ ModuleWidget *RackWidget::moduleFromJson(json_t *moduleJ) { | |||
std::string modelSlug = json_string_value(modelSlugJ); | |||
// Get Model | |||
Model *model = context()->plugin->getModel(pluginSlug, modelSlug); | |||
Model *model = plugin::getModel(pluginSlug, modelSlug); | |||
if (!model) | |||
return NULL; | |||
@@ -26,9 +26,9 @@ namespace asset { | |||
void init(bool devMode) { | |||
// Get system dir | |||
if (gSystemDir.empty()) { | |||
if (systemDir.empty()) { | |||
if (devMode) { | |||
gSystemDir = "."; | |||
systemDir = "."; | |||
} | |||
else { | |||
#if ARCH_MAC | |||
@@ -39,26 +39,26 @@ void init(bool devMode) { | |||
Boolean success = CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8*) resourcesBuf, sizeof(resourcesBuf)); | |||
assert(success); | |||
CFRelease(resourcesUrl); | |||
gSystemDir = resourcesBuf; | |||
systemDir = resourcesBuf; | |||
#endif | |||
#if ARCH_WIN | |||
char moduleBuf[MAX_PATH]; | |||
DWORD length = GetModuleFileName(NULL, moduleBuf, sizeof(moduleBuf)); | |||
assert(length > 0); | |||
PathRemoveFileSpec(moduleBuf); | |||
gSystemDir = moduleBuf; | |||
systemDir = moduleBuf; | |||
#endif | |||
#if ARCH_LIN | |||
// TODO For now, users should launch Rack from their terminal in the system directory | |||
gSystemDir = "."; | |||
systemDir = "."; | |||
#endif | |||
} | |||
} | |||
// Get user dir | |||
if (gUserDir.empty()) { | |||
if (userDir.empty()) { | |||
if (devMode) { | |||
gUserDir = "."; | |||
userDir = "."; | |||
} | |||
else { | |||
#if ARCH_WIN | |||
@@ -66,15 +66,15 @@ void init(bool devMode) { | |||
char documentsBuf[MAX_PATH]; | |||
HRESULT result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, documentsBuf); | |||
assert(result == S_OK); | |||
gUserDir = documentsBuf; | |||
gUserDir += "/Rack"; | |||
userDir = documentsBuf; | |||
userDir += "/Rack"; | |||
#endif | |||
#if ARCH_MAC | |||
// Get home directory | |||
struct passwd *pw = getpwuid(getuid()); | |||
assert(pw); | |||
gUserDir = pw->pw_dir; | |||
gUserDir += "/Documents/Rack"; | |||
userDir = pw->pw_dir; | |||
userDir += "/Documents/Rack"; | |||
#endif | |||
#if ARCH_LIN | |||
// Get home directory | |||
@@ -84,24 +84,24 @@ void init(bool devMode) { | |||
assert(pw); | |||
homeBuf = pw->pw_dir; | |||
} | |||
gUserDir = homeBuf; | |||
gUserDir += "/.Rack"; | |||
userDir = homeBuf; | |||
userDir += "/.Rack"; | |||
#endif | |||
} | |||
} | |||
system::createDirectory(gSystemDir); | |||
system::createDirectory(gUserDir); | |||
system::createDirectory(systemDir); | |||
system::createDirectory(userDir); | |||
} | |||
std::string system(std::string filename) { | |||
return gSystemDir + "/" + filename; | |||
return systemDir + "/" + filename; | |||
} | |||
std::string user(std::string filename) { | |||
return gUserDir + "/" + filename; | |||
return userDir + "/" + filename; | |||
} | |||
@@ -111,8 +111,8 @@ std::string plugin(Plugin *plugin, std::string filename) { | |||
} | |||
std::string gSystemDir; | |||
std::string gUserDir; | |||
std::string systemDir; | |||
std::string userDir; | |||
} // namespace asset | |||
@@ -10,7 +10,7 @@ | |||
#include "engine/Engine.hpp" | |||
#include "app/Scene.hpp" | |||
#include "tags.hpp" | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "context.hpp" | |||
#include "ui.hpp" | |||
@@ -48,10 +48,10 @@ int main(int argc, char *argv[]) { | |||
devMode = true; | |||
} break; | |||
case 's': { | |||
asset::gSystemDir = optarg; | |||
asset::systemDir = optarg; | |||
} break; | |||
case 'u': { | |||
asset::gUserDir = optarg; | |||
asset::userDir = optarg; | |||
} break; | |||
default: break; | |||
} | |||
@@ -70,16 +70,16 @@ int main(int argc, char *argv[]) { | |||
keyboard::init(); | |||
gamepad::init(); | |||
uiInit(); | |||
plugin::init(devMode); | |||
// Log environment | |||
INFO("%s %s", APP_NAME.c_str(), APP_VERSION.c_str()); | |||
if (devMode) | |||
INFO("Development mode"); | |||
INFO("System directory: %s", asset::gSystemDir.c_str()); | |||
INFO("User directory: %s", asset::gUserDir.c_str()); | |||
INFO("System directory: %s", asset::systemDir.c_str()); | |||
INFO("User directory: %s", asset::userDir.c_str()); | |||
// Initialize app | |||
context()->plugin = new PluginManager(devMode); | |||
context()->engine = new Engine; | |||
context()->event = new event::Context; | |||
context()->scene = new Scene; | |||
@@ -125,10 +125,9 @@ int main(int argc, char *argv[]) { | |||
context()->window = NULL; | |||
delete context()->engine; | |||
context()->engine = NULL; | |||
delete context()->plugin; | |||
context()->plugin = NULL; | |||
// Destroy environment | |||
plugin::destroy(); | |||
uiDestroy(); | |||
bridgeDestroy(); | |||
midiDestroy(); | |||
@@ -1,4 +1,4 @@ | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "system.hpp" | |||
#include "logger.hpp" | |||
#include "network.hpp" | |||
@@ -6,6 +6,7 @@ | |||
#include "string.hpp" | |||
#include "context.hpp" | |||
#include "app/common.hpp" | |||
#include "plugin/callbacks.hpp" | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
@@ -31,13 +32,14 @@ | |||
namespace rack { | |||
namespace plugin { | |||
//////////////////// | |||
// private API | |||
//////////////////// | |||
static bool PluginManager_loadPlugin(PluginManager *pluginManager, std::string path) { | |||
static bool loadPlugin(std::string path) { | |||
std::string libraryFilename; | |||
#if ARCH_LIN | |||
libraryFilename = path + "/" + "plugin.so"; | |||
@@ -91,7 +93,7 @@ static bool PluginManager_loadPlugin(PluginManager *pluginManager, std::string p | |||
initCallback(plugin); | |||
// Reject plugin if slug already exists | |||
Plugin *oldPlugin = pluginManager->getPlugin(plugin->slug); | |||
Plugin *oldPlugin = getPlugin(plugin->slug); | |||
if (oldPlugin) { | |||
WARN("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str()); | |||
// TODO | |||
@@ -100,13 +102,13 @@ static bool PluginManager_loadPlugin(PluginManager *pluginManager, std::string p | |||
} | |||
// Add plugin to list | |||
pluginManager->plugins.push_back(plugin); | |||
plugins.push_back(plugin); | |||
INFO("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str()); | |||
return true; | |||
} | |||
static bool PluginManager_syncPlugin(PluginManager *pluginManager, std::string slug, json_t *manifestJ, bool dryRun) { | |||
static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||
// Check that "status" is "available" | |||
json_t *statusJ = json_object_get(manifestJ, "status"); | |||
if (!statusJ) { | |||
@@ -126,7 +128,7 @@ static bool PluginManager_syncPlugin(PluginManager *pluginManager, std::string s | |||
std::string latestVersion = json_string_value(latestVersionJ); | |||
// Check whether we already have a plugin with the same slug and version | |||
Plugin *plugin = pluginManager->getPlugin(slug); | |||
Plugin *plugin = getPlugin(slug); | |||
if (plugin && plugin->version == latestVersion) { | |||
return false; | |||
} | |||
@@ -154,7 +156,7 @@ static bool PluginManager_syncPlugin(PluginManager *pluginManager, std::string s | |||
if (dryRun) { | |||
downloadUrl += "/available"; | |||
} | |||
downloadUrl += "?token=" + network::encodeUrl(pluginManager->token); | |||
downloadUrl += "?token=" + network::encodeUrl(token); | |||
downloadUrl += "&slug=" + network::encodeUrl(slug); | |||
downloadUrl += "&version=" + network::encodeUrl(latestVersion); | |||
downloadUrl += "&arch=" + network::encodeUrl(arch); | |||
@@ -173,28 +175,28 @@ static bool PluginManager_syncPlugin(PluginManager *pluginManager, std::string s | |||
return json_boolean_value(successJ); | |||
} | |||
else { | |||
pluginManager->downloadName = name; | |||
pluginManager->downloadProgress = 0.0; | |||
downloadName = name; | |||
downloadProgress = 0.0; | |||
INFO("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str()); | |||
// Download zip | |||
std::string pluginDest = asset::user("plugins/" + slug + ".zip"); | |||
if (!network::requestDownload(downloadUrl, pluginDest, &pluginManager->downloadProgress)) { | |||
if (!network::requestDownload(downloadUrl, pluginDest, &downloadProgress)) { | |||
WARN("Plugin %s download was unsuccessful", slug.c_str()); | |||
return false; | |||
} | |||
pluginManager->downloadName = ""; | |||
downloadName = ""; | |||
return true; | |||
} | |||
} | |||
static void PluginManager_loadPlugins(PluginManager *pluginManager, std::string path) { | |||
static void loadPlugins(std::string path) { | |||
std::string message; | |||
for (std::string pluginPath : system::listEntries(path)) { | |||
if (!system::isDirectory(pluginPath)) | |||
continue; | |||
if (!PluginManager_loadPlugin(pluginManager, pluginPath)) { | |||
if (!loadPlugin(pluginPath)) { | |||
message += string::f("Could not load plugin %s\n", pluginPath.c_str()); | |||
} | |||
} | |||
@@ -300,18 +302,18 @@ static void extractPackages(std::string path) { | |||
// public API | |||
//////////////////// | |||
PluginManager::PluginManager(bool devMode) { | |||
void init(bool devMode) { | |||
// Load core | |||
// This function is defined in core.cpp | |||
Plugin *corePlugin = new Plugin; | |||
init(corePlugin); | |||
::init(corePlugin); | |||
plugins.push_back(corePlugin); | |||
// Get user plugins directory | |||
std::string userPlugins = asset::user("plugins"); | |||
mkdir(userPlugins.c_str(), 0755); | |||
if (devMode) { | |||
if (!devMode) { | |||
// Copy Fundamental package to plugins directory if folder does not exist | |||
std::string fundamentalSrc = asset::system("Fundamental.zip"); | |||
std::string fundamentalDest = asset::user("plugins/Fundamental.zip"); | |||
@@ -323,10 +325,10 @@ PluginManager::PluginManager(bool devMode) { | |||
// Extract packages and load plugins | |||
extractPackages(userPlugins); | |||
PluginManager_loadPlugins(this, userPlugins); | |||
loadPlugins(userPlugins); | |||
} | |||
PluginManager::~PluginManager() { | |||
void destroy() { | |||
for (Plugin *plugin : plugins) { | |||
// Free library handle | |||
#if ARCH_WIN | |||
@@ -344,7 +346,7 @@ PluginManager::~PluginManager() { | |||
plugins.clear(); | |||
} | |||
void PluginManager::logIn(std::string email, std::string password) { | |||
void logIn(std::string email, std::string password) { | |||
json_t *reqJ = json_object(); | |||
json_object_set(reqJ, "email", json_string(email.c_str())); | |||
json_object_set(reqJ, "password", json_string(password.c_str())); | |||
@@ -369,11 +371,11 @@ void PluginManager::logIn(std::string email, std::string password) { | |||
} | |||
} | |||
void PluginManager::logOut() { | |||
void logOut() { | |||
token = ""; | |||
} | |||
bool PluginManager::sync(bool dryRun) { | |||
bool sync(bool dryRun) { | |||
if (token.empty()) | |||
return false; | |||
@@ -444,7 +446,7 @@ bool PluginManager::sync(bool dryRun) { | |||
if (!manifestJ) | |||
continue; | |||
if (PluginManager_syncPlugin(this, slug, manifestJ, dryRun)) { | |||
if (syncPlugin(slug, manifestJ, dryRun)) { | |||
available = true; | |||
} | |||
} | |||
@@ -452,15 +454,15 @@ bool PluginManager::sync(bool dryRun) { | |||
return available; | |||
} | |||
void PluginManager::cancelDownload() { | |||
void cancelDownload() { | |||
// TODO | |||
} | |||
bool PluginManager::isLoggedIn() { | |||
bool isLoggedIn() { | |||
return token != ""; | |||
} | |||
Plugin *PluginManager::getPlugin(std::string pluginSlug) { | |||
Plugin *getPlugin(std::string pluginSlug) { | |||
for (Plugin *plugin : plugins) { | |||
if (plugin->slug == pluginSlug) { | |||
return plugin; | |||
@@ -469,7 +471,7 @@ Plugin *PluginManager::getPlugin(std::string pluginSlug) { | |||
return NULL; | |||
} | |||
Model *PluginManager::getModel(std::string pluginSlug, std::string modelSlug) { | |||
Model *getModel(std::string pluginSlug, std::string modelSlug) { | |||
Plugin *plugin = getPlugin(pluginSlug); | |||
if (plugin) { | |||
for (Model *model : plugin->models) { | |||
@@ -482,4 +484,13 @@ Model *PluginManager::getModel(std::string pluginSlug, std::string modelSlug) { | |||
} | |||
std::list<Plugin*> plugins; | |||
std::string token; | |||
bool isDownloading = false; | |||
float downloadProgress = 0.f; | |||
std::string downloadName; | |||
std::string loginStatus; | |||
} // namespace plugin | |||
} // namespace rack |
@@ -1,7 +1,7 @@ | |||
#include "settings.hpp" | |||
#include "logger.hpp" | |||
#include "window.hpp" | |||
#include "plugin/PluginManager.hpp" | |||
#include "plugin.hpp" | |||
#include "app/Scene.hpp" | |||
#include "app/ModuleBrowser.hpp" | |||
#include "engine/Engine.hpp" | |||
@@ -21,7 +21,7 @@ static json_t *settingsToJson() { | |||
json_t *rootJ = json_object(); | |||
// token | |||
json_t *tokenJ = json_string(context()->plugin->token.c_str()); | |||
json_t *tokenJ = json_string(plugin::token.c_str()); | |||
json_object_set_new(rootJ, "token", tokenJ); | |||
if (!context()->window->isMaximized()) { | |||
@@ -84,7 +84,7 @@ static void settingsFromJson(json_t *rootJ) { | |||
// token | |||
json_t *tokenJ = json_object_get(rootJ, "token"); | |||
if (tokenJ) | |||
context()->plugin->token = json_string_value(tokenJ); | |||
plugin::token = json_string_value(tokenJ); | |||
// windowSize | |||
json_t *windowSizeJ = json_object_get(rootJ, "windowSize"); | |||