@@ -5,7 +5,7 @@ | |||||
#include "app/SVGPanel.hpp" | #include "app/SVGPanel.hpp" | ||||
#include "app/Port.hpp" | #include "app/Port.hpp" | ||||
#include "app/ParamWidget.hpp" | #include "app/ParamWidget.hpp" | ||||
#include "plugin.hpp" | |||||
#include "plugin/Model.hpp" | |||||
#include "engine/Module.hpp" | #include "engine/Module.hpp" | ||||
@@ -1,6 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "plugin.hpp" | |||||
#include "plugin/Plugin.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -1,4 +1,4 @@ | |||||
#include "plugin.hpp" | |||||
#include "plugin/Model.hpp" | |||||
#include "event.hpp" | #include "event.hpp" | ||||
#include "ui/MenuLabel.hpp" | #include "ui/MenuLabel.hpp" | ||||
#include "ui/MenuItem.hpp" | #include "ui/MenuItem.hpp" | ||||
@@ -1,102 +0,0 @@ | |||||
#pragma once | |||||
#include <string> | |||||
#include <list> | |||||
#include "common.hpp" | |||||
#include "tags.hpp" | |||||
namespace rack { | |||||
struct ModuleWidget; | |||||
struct Module; | |||||
struct Model; | |||||
// Subclass this and return a pointer to a new one when init() is called | |||||
struct Plugin { | |||||
/** A list of the models available by this plugin, add with addModel() */ | |||||
std::list<Model*> models; | |||||
/** The file path of the plugin's directory */ | |||||
std::string path; | |||||
/** OS-dependent library handle */ | |||||
void *handle = NULL; | |||||
/** Must be unique. Used for patch files and the VCV store API. | |||||
To guarantee uniqueness, it is a good idea to prefix the slug by your "company name" if available, e.g. "MyCompany-MyPlugin" | |||||
*/ | |||||
std::string slug; | |||||
/** The version of your plugin | |||||
Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266 | |||||
Do not include the "v" in "v1.0" for example. | |||||
*/ | |||||
std::string version; | |||||
/** Deprecated, do not use. */ | |||||
std::string website; | |||||
std::string manual; | |||||
virtual ~Plugin(); | |||||
void addModel(Model *model); | |||||
}; | |||||
struct Model { | |||||
Plugin *plugin = NULL; | |||||
/** An identifier for the model, e.g. "VCO". Used for saving patches. | |||||
The model slug must be unique in your plugin, but it doesn't need to be unique among different plugins. | |||||
*/ | |||||
std::string slug; | |||||
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | |||||
std::string name; | |||||
/** The author name of the module. | |||||
This might be different than the plugin slug. For example, if you create multiple plugins but want them to be branded similarly, you may use the same author in multiple plugins. | |||||
You may even have multiple authors in one plugin, although this property will be moved to Plugin for Rack 1.0. | |||||
*/ | |||||
std::string author; | |||||
/** List of tags representing the function(s) of the module (optional) */ | |||||
std::list<ModelTag> tags; | |||||
virtual ~Model() {} | |||||
/** Creates a headless Module */ | |||||
virtual Module *createModule() { return NULL; } | |||||
/** Creates a ModuleWidget with a Module attached */ | |||||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||||
/** Creates a ModuleWidget with no Module, useful for previews */ | |||||
virtual ModuleWidget *createModuleWidgetNull() { return NULL; } | |||||
}; | |||||
void pluginInit(bool devMode); | |||||
void pluginDestroy(); | |||||
void pluginLogIn(std::string email, std::string password); | |||||
void pluginLogOut(); | |||||
/** Returns whether a new plugin is available, and downloads it unless doing a dry run */ | |||||
bool pluginSync(bool dryRun); | |||||
void pluginCancelDownload(); | |||||
bool pluginIsLoggedIn(); | |||||
bool pluginIsDownloading(); | |||||
float pluginGetDownloadProgress(); | |||||
std::string pluginGetDownloadName(); | |||||
std::string pluginGetLoginStatus(); | |||||
Plugin *pluginGetPlugin(std::string pluginSlug); | |||||
Model *pluginGetModel(std::string pluginSlug, std::string modelSlug); | |||||
extern std::list<Plugin*> gPlugins; | |||||
extern std::string gToken; | |||||
} // 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); |
@@ -0,0 +1,41 @@ | |||||
#pragma once | |||||
#include "common.hpp" | |||||
#include "plugin/Plugin.hpp" | |||||
#include "tags.hpp" | |||||
#include <list> | |||||
namespace rack { | |||||
struct ModuleWidget; | |||||
struct Module; | |||||
struct Model { | |||||
Plugin *plugin = NULL; | |||||
/** An identifier for the model, e.g. "VCO". Used for saving patches. | |||||
The model slug must be unique in your plugin, but it doesn't need to be unique among different plugins. | |||||
*/ | |||||
std::string slug; | |||||
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | |||||
std::string name; | |||||
/** The author name of the module. | |||||
This might be different than the plugin slug. For example, if you create multiple plugins but want them to be branded similarly, you may use the same author in multiple plugins. | |||||
You may even have multiple authors in one plugin, although this property will be moved to Plugin for Rack 1.0. | |||||
*/ | |||||
std::string author; | |||||
/** List of tags representing the function(s) of the module (optional) */ | |||||
std::list<ModelTag> tags; | |||||
virtual ~Model() {} | |||||
/** Creates a headless Module */ | |||||
virtual Module *createModule() { return NULL; } | |||||
/** Creates a ModuleWidget with a Module attached */ | |||||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||||
/** Creates a ModuleWidget with no Module, useful for previews */ | |||||
virtual ModuleWidget *createModuleWidgetNull() { return NULL; } | |||||
}; | |||||
} // namespace rack |
@@ -0,0 +1,41 @@ | |||||
#pragma once | |||||
#include "common.hpp" | |||||
#include <list> | |||||
namespace rack { | |||||
struct Model; | |||||
// Subclass this and return a pointer to a new one when init() is called | |||||
struct Plugin { | |||||
/** A list of the models available by this plugin, add with addModel() */ | |||||
std::list<Model*> models; | |||||
/** The file path of the plugin's directory */ | |||||
std::string path; | |||||
/** OS-dependent library handle */ | |||||
void *handle = NULL; | |||||
/** Must be unique. Used for patch files and the VCV store API. | |||||
To guarantee uniqueness, it is a good idea to prefix the slug by your "company name" if available, e.g. "MyCompany-MyPlugin" | |||||
*/ | |||||
std::string slug; | |||||
/** The version of your plugin | |||||
Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266 | |||||
Do not include the "v" in "v1.0" for example. | |||||
*/ | |||||
std::string version; | |||||
/** Deprecated, do not use. */ | |||||
std::string website; | |||||
std::string manual; | |||||
virtual ~Plugin(); | |||||
void addModel(Model *model); | |||||
}; | |||||
} // namespace rack |
@@ -0,0 +1,46 @@ | |||||
#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); | |||||
}; | |||||
extern PluginManager *gPluginManager; | |||||
} // 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); |
@@ -0,0 +1,9 @@ | |||||
#pragma once | |||||
#include "plugin/Plugin.hpp" | |||||
/** Called once to initialize and return the Plugin instance. | |||||
You must implement this in your plugin | |||||
*/ | |||||
extern "C" | |||||
void init(rack::Plugin *plugin); |
@@ -8,7 +8,6 @@ | |||||
#include "random.hpp" | #include "random.hpp" | ||||
#include "network.hpp" | #include "network.hpp" | ||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "plugin.hpp" | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "helpers.hpp" | #include "helpers.hpp" | ||||
@@ -77,3 +76,7 @@ | |||||
#include "engine/Output.hpp" | #include "engine/Output.hpp" | ||||
#include "engine/Param.hpp" | #include "engine/Param.hpp" | ||||
#include "engine/Wire.hpp" | #include "engine/Wire.hpp" | ||||
#include "plugin/Plugin.hpp" | |||||
#include "plugin/Model.hpp" | |||||
#include "plugin/callbacks.hpp" |
@@ -1,6 +1,5 @@ | |||||
#include <set> | #include <set> | ||||
#include <algorithm> | #include <algorithm> | ||||
#include "plugin.hpp" | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "helpers.hpp" | #include "helpers.hpp" | ||||
#include "event.hpp" | #include "event.hpp" | ||||
@@ -11,6 +10,7 @@ | |||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "ui/List.hpp" | #include "ui/List.hpp" | ||||
#include "ui/TextField.hpp" | #include "ui/TextField.hpp" | ||||
#include "plugin/PluginManager.hpp" | |||||
static const float itemMargin = 2.0; | static const float itemMargin = 2.0; | ||||
@@ -322,7 +322,7 @@ struct ModuleBrowser : OpaqueWidget { | |||||
addChild(moduleScroll); | addChild(moduleScroll); | ||||
// Collect authors | // Collect authors | ||||
for (Plugin *plugin : gPlugins) { | |||||
for (Plugin *plugin : gPluginManager->plugins) { | |||||
for (Model *model : plugin->models) { | for (Model *model : plugin->models) { | ||||
// Insert author | // Insert author | ||||
if (!model->author.empty()) | if (!model->author.empty()) | ||||
@@ -427,7 +427,7 @@ struct ModuleBrowser : OpaqueWidget { | |||||
moduleList->addChild(item); | moduleList->addChild(item); | ||||
} | } | ||||
// Modules | // Modules | ||||
for (Plugin *plugin : gPlugins) { | |||||
for (Plugin *plugin : gPluginManager->plugins) { | |||||
for (Model *model : plugin->models) { | for (Model *model : plugin->models) { | ||||
if (isModelFiltered(model) && isModelMatch(model, search)) { | if (isModelFiltered(model) && isModelMatch(model, search)) { | ||||
ModelItem *item = new ModelItem; | ModelItem *item = new ModelItem; | ||||
@@ -587,7 +587,7 @@ void moduleBrowserFromJson(json_t *rootJ) { | |||||
continue; | continue; | ||||
std::string pluginSlug = json_string_value(pluginJ); | std::string pluginSlug = json_string_value(pluginJ); | ||||
std::string modelSlug = json_string_value(modelJ); | std::string modelSlug = json_string_value(modelJ); | ||||
Model *model = pluginGetModel(pluginSlug, modelSlug); | |||||
Model *model = gPluginManager->getModel(pluginSlug, modelSlug); | |||||
if (!model) | if (!model) | ||||
continue; | continue; | ||||
sFavoriteModels.insert(model); | sFavoriteModels.insert(model); | ||||
@@ -7,7 +7,7 @@ | |||||
#include "ui/TextField.hpp" | #include "ui/TextField.hpp" | ||||
#include "ui/PasswordField.hpp" | #include "ui/PasswordField.hpp" | ||||
#include "ui/Label.hpp" | #include "ui/Label.hpp" | ||||
#include "plugin.hpp" | |||||
#include "plugin/PluginManager.hpp" | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "helpers.hpp" | #include "helpers.hpp" | ||||
#include "osdialog.h" | #include "osdialog.h" | ||||
@@ -30,7 +30,9 @@ struct LogInButton : Button { | |||||
TextField *emailField; | TextField *emailField; | ||||
TextField *passwordField; | TextField *passwordField; | ||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
std::thread t(pluginLogIn, emailField->text, passwordField->text); | |||||
std::thread t([&]() { | |||||
gPluginManager->logIn(emailField->text, passwordField->text); | |||||
}); | |||||
t.detach(); | t.detach(); | ||||
passwordField->text = ""; | passwordField->text = ""; | ||||
} | } | ||||
@@ -39,7 +41,7 @@ struct LogInButton : Button { | |||||
struct StatusLabel : Label { | struct StatusLabel : Label { | ||||
void step() override { | void step() override { | ||||
text = pluginGetLoginStatus(); | |||||
text = gPluginManager->loginStatus; | |||||
} | } | ||||
}; | }; | ||||
@@ -65,7 +67,7 @@ struct SyncButton : Button { | |||||
// Check for plugin update on first step() | // Check for plugin update on first step() | ||||
if (!checked) { | if (!checked) { | ||||
std::thread t([this]() { | std::thread t([this]() { | ||||
if (pluginSync(true)) | |||||
if (gPluginManager->sync(true)) | |||||
available = true; | available = true; | ||||
}); | }); | ||||
t.detach(); | t.detach(); | ||||
@@ -94,7 +96,7 @@ struct SyncButton : Button { | |||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
available = false; | available = false; | ||||
std::thread t([this]() { | std::thread t([this]() { | ||||
if (pluginSync(false)) | |||||
if (gPluginManager->sync(false)) | |||||
completed = true; | completed = true; | ||||
}); | }); | ||||
t.detach(); | t.detach(); | ||||
@@ -104,14 +106,14 @@ struct SyncButton : Button { | |||||
struct LogOutButton : Button { | struct LogOutButton : Button { | ||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
pluginLogOut(); | |||||
gPluginManager->logOut(); | |||||
} | } | ||||
}; | }; | ||||
struct DownloadQuantity : Quantity { | struct DownloadQuantity : Quantity { | ||||
float getValue() override { | float getValue() override { | ||||
return pluginGetDownloadProgress(); | |||||
return gPluginManager->downloadProgress; | |||||
} | } | ||||
float getDisplayValue() override { | float getDisplayValue() override { | ||||
@@ -121,7 +123,7 @@ struct DownloadQuantity : Quantity { | |||||
int getDisplayPrecision() override {return 0;} | int getDisplayPrecision() override {return 0;} | ||||
std::string getLabel() override { | std::string getLabel() override { | ||||
return "Downloading " + pluginGetDownloadName(); | |||||
return "Downloading " + gPluginManager->downloadName; | |||||
} | } | ||||
std::string getUnit() override {return "%";} | std::string getUnit() override {return "%";} | ||||
@@ -137,7 +139,7 @@ struct DownloadProgressBar : ProgressBar { | |||||
struct CancelButton : Button { | struct CancelButton : Button { | ||||
void onAction(event::Action &e) override { | void onAction(event::Action &e) override { | ||||
pluginCancelDownload(); | |||||
gPluginManager->cancelDownload(); | |||||
} | } | ||||
}; | }; | ||||
@@ -224,9 +226,9 @@ void PluginManagerWidget::step() { | |||||
manageWidget->visible = false; | manageWidget->visible = false; | ||||
downloadWidget->visible = false; | downloadWidget->visible = false; | ||||
if (pluginIsDownloading()) | |||||
if (gPluginManager->isDownloading) | |||||
downloadWidget->visible = true; | downloadWidget->visible = true; | ||||
else if (pluginIsLoggedIn()) | |||||
else if (gPluginManager->isLoggedIn()) | |||||
manageWidget->visible = true; | manageWidget->visible = true; | ||||
else | else | ||||
loginWidget->visible = true; | loginWidget->visible = true; | ||||
@@ -9,6 +9,7 @@ | |||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "system.hpp" | #include "system.hpp" | ||||
#include "logger.hpp" | #include "logger.hpp" | ||||
#include "plugin/PluginManager.hpp" | |||||
namespace rack { | namespace rack { | ||||
@@ -377,7 +378,7 @@ ModuleWidget *RackWidget::moduleFromJson(json_t *moduleJ) { | |||||
std::string modelSlug = json_string_value(modelSlugJ); | std::string modelSlug = json_string_value(modelSlugJ); | ||||
// Get Model | // Get Model | ||||
Model *model = pluginGetModel(pluginSlug, modelSlug); | |||||
Model *model = gPluginManager->getModel(pluginSlug, modelSlug); | |||||
if (!model) | if (!model) | ||||
return NULL; | return NULL; | ||||
@@ -10,6 +10,8 @@ | |||||
#include "settings.hpp" | #include "settings.hpp" | ||||
#include "engine/Engine.hpp" | #include "engine/Engine.hpp" | ||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "tags.hpp" | |||||
#include "plugin/PluginManager.hpp" | |||||
#ifdef ARCH_WIN | #ifdef ARCH_WIN | ||||
#include <Windows.h> | #include <Windows.h> | ||||
@@ -67,7 +69,8 @@ int main(int argc, char *argv[]) { | |||||
INFO("Local directory: %s", asset::local("").c_str()); | INFO("Local directory: %s", asset::local("").c_str()); | ||||
// Initialize app | // Initialize app | ||||
pluginInit(devMode); | |||||
tagsInit(); | |||||
gPluginManager = new PluginManager(devMode); | |||||
gEngine = new Engine; | gEngine = new Engine; | ||||
rtmidiInit(); | rtmidiInit(); | ||||
bridgeInit(); | bridgeInit(); | ||||
@@ -118,7 +121,8 @@ int main(int argc, char *argv[]) { | |||||
delete gEngine; | delete gEngine; | ||||
gEngine = NULL; | gEngine = NULL; | ||||
midiDestroy(); | midiDestroy(); | ||||
pluginDestroy(); | |||||
delete gPluginManager; | |||||
gPluginManager = NULL; | |||||
logger::destroy(); | logger::destroy(); | ||||
return 0; | return 0; | ||||
@@ -0,0 +1,21 @@ | |||||
#include "plugin/Plugin.hpp" | |||||
#include "plugin/Model.hpp" | |||||
namespace rack { | |||||
Plugin::~Plugin() { | |||||
for (Model *model : models) { | |||||
delete model; | |||||
} | |||||
} | |||||
void Plugin::addModel(Model *model) { | |||||
assert(!model->plugin); | |||||
model->plugin = this; | |||||
models.push_back(model); | |||||
} | |||||
} // namespace rack |
@@ -19,7 +19,7 @@ | |||||
#endif | #endif | ||||
#include <dirent.h> | #include <dirent.h> | ||||
#include "plugin.hpp" | |||||
#include "plugin/PluginManager.hpp" | |||||
#include "system.hpp" | #include "system.hpp" | ||||
#include "logger.hpp" | #include "logger.hpp" | ||||
#include "network.hpp" | #include "network.hpp" | ||||
@@ -32,33 +32,11 @@ | |||||
namespace rack { | namespace rack { | ||||
std::list<Plugin*> gPlugins; | |||||
std::string gToken; | |||||
static bool isDownloading = false; | |||||
static float downloadProgress = 0.0; | |||||
static std::string downloadName; | |||||
static std::string loginStatus; | |||||
Plugin::~Plugin() { | |||||
for (Model *model : models) { | |||||
delete model; | |||||
} | |||||
} | |||||
void Plugin::addModel(Model *model) { | |||||
assert(!model->plugin); | |||||
model->plugin = this; | |||||
models.push_back(model); | |||||
} | |||||
//////////////////// | //////////////////// | ||||
// private API | // private API | ||||
//////////////////// | //////////////////// | ||||
static bool loadPlugin(std::string path) { | |||||
static bool PluginManager_loadPlugin(PluginManager *pluginManager, std::string path) { | |||||
std::string libraryFilename; | std::string libraryFilename; | ||||
#if ARCH_LIN | #if ARCH_LIN | ||||
libraryFilename = path + "/" + "plugin.so"; | libraryFilename = path + "/" + "plugin.so"; | ||||
@@ -112,7 +90,7 @@ static bool loadPlugin(std::string path) { | |||||
initCallback(plugin); | initCallback(plugin); | ||||
// Reject plugin if slug already exists | // Reject plugin if slug already exists | ||||
Plugin *oldPlugin = pluginGetPlugin(plugin->slug); | |||||
Plugin *oldPlugin = pluginManager->getPlugin(plugin->slug); | |||||
if (oldPlugin) { | if (oldPlugin) { | ||||
WARN("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str()); | WARN("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str()); | ||||
// TODO | // TODO | ||||
@@ -121,13 +99,13 @@ static bool loadPlugin(std::string path) { | |||||
} | } | ||||
// Add plugin to list | // Add plugin to list | ||||
gPlugins.push_back(plugin); | |||||
pluginManager->plugins.push_back(plugin); | |||||
INFO("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str()); | INFO("Loaded plugin %s %s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str()); | ||||
return true; | return true; | ||||
} | } | ||||
static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
static bool PluginManager_syncPlugin(PluginManager *pluginManager, std::string slug, json_t *manifestJ, bool dryRun) { | |||||
// Check that "status" is "available" | // Check that "status" is "available" | ||||
json_t *statusJ = json_object_get(manifestJ, "status"); | json_t *statusJ = json_object_get(manifestJ, "status"); | ||||
if (!statusJ) { | if (!statusJ) { | ||||
@@ -147,7 +125,7 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
std::string latestVersion = json_string_value(latestVersionJ); | std::string latestVersion = json_string_value(latestVersionJ); | ||||
// Check whether we already have a plugin with the same slug and version | // Check whether we already have a plugin with the same slug and version | ||||
Plugin *plugin = pluginGetPlugin(slug); | |||||
Plugin *plugin = pluginManager->getPlugin(slug); | |||||
if (plugin && plugin->version == latestVersion) { | if (plugin && plugin->version == latestVersion) { | ||||
return false; | return false; | ||||
} | } | ||||
@@ -175,7 +153,7 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
if (dryRun) { | if (dryRun) { | ||||
downloadUrl += "/available"; | downloadUrl += "/available"; | ||||
} | } | ||||
downloadUrl += "?token=" + network::encodeUrl(gToken); | |||||
downloadUrl += "?token=" + network::encodeUrl(pluginManager->token); | |||||
downloadUrl += "&slug=" + network::encodeUrl(slug); | downloadUrl += "&slug=" + network::encodeUrl(slug); | ||||
downloadUrl += "&version=" + network::encodeUrl(latestVersion); | downloadUrl += "&version=" + network::encodeUrl(latestVersion); | ||||
downloadUrl += "&arch=" + network::encodeUrl(arch); | downloadUrl += "&arch=" + network::encodeUrl(arch); | ||||
@@ -194,28 +172,28 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
return json_boolean_value(successJ); | return json_boolean_value(successJ); | ||||
} | } | ||||
else { | else { | ||||
downloadName = name; | |||||
downloadProgress = 0.0; | |||||
pluginManager->downloadName = name; | |||||
pluginManager->downloadProgress = 0.0; | |||||
INFO("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str()); | INFO("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str()); | ||||
// Download zip | // Download zip | ||||
std::string pluginDest = asset::local("plugins/" + slug + ".zip"); | std::string pluginDest = asset::local("plugins/" + slug + ".zip"); | ||||
if (!network::requestDownload(downloadUrl, pluginDest, &downloadProgress)) { | |||||
if (!network::requestDownload(downloadUrl, pluginDest, &pluginManager->downloadProgress)) { | |||||
WARN("Plugin %s download was unsuccessful", slug.c_str()); | WARN("Plugin %s download was unsuccessful", slug.c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
downloadName = ""; | |||||
pluginManager->downloadName = ""; | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
static void loadPlugins(std::string path) { | |||||
static void PluginManager_loadPlugins(PluginManager *pluginManager, std::string path) { | |||||
std::string message; | std::string message; | ||||
for (std::string pluginPath : system::listEntries(path)) { | for (std::string pluginPath : system::listEntries(path)) { | ||||
if (!system::isDirectory(pluginPath)) | if (!system::isDirectory(pluginPath)) | ||||
continue; | continue; | ||||
if (!loadPlugin(pluginPath)) { | |||||
if (!PluginManager_loadPlugin(pluginManager, pluginPath)) { | |||||
message += string::f("Could not load plugin %s\n", pluginPath.c_str()); | message += string::f("Could not load plugin %s\n", pluginPath.c_str()); | ||||
} | } | ||||
} | } | ||||
@@ -321,14 +299,12 @@ static void extractPackages(std::string path) { | |||||
// public API | // public API | ||||
//////////////////// | //////////////////// | ||||
void pluginInit(bool devMode) { | |||||
tagsInit(); | |||||
PluginManager::PluginManager(bool devMode) { | |||||
// Load core | // Load core | ||||
// This function is defined in core.cpp | // This function is defined in core.cpp | ||||
Plugin *corePlugin = new Plugin; | Plugin *corePlugin = new Plugin; | ||||
init(corePlugin); | init(corePlugin); | ||||
gPlugins.push_back(corePlugin); | |||||
plugins.push_back(corePlugin); | |||||
// Get local plugins directory | // Get local plugins directory | ||||
std::string localPlugins = asset::local("plugins"); | std::string localPlugins = asset::local("plugins"); | ||||
@@ -346,15 +322,15 @@ void pluginInit(bool devMode) { | |||||
// Extract packages and load plugins | // Extract packages and load plugins | ||||
extractPackages(localPlugins); | extractPackages(localPlugins); | ||||
loadPlugins(localPlugins); | |||||
PluginManager_loadPlugins(this, localPlugins); | |||||
} | } | ||||
void pluginDestroy() { | |||||
for (Plugin *plugin : gPlugins) { | |||||
PluginManager::~PluginManager() { | |||||
for (Plugin *plugin : plugins) { | |||||
// Free library handle | // Free library handle | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
if (plugin->handle) | if (plugin->handle) | ||||
FreeLibrary((HINSTANCE)plugin->handle); | |||||
FreeLibrary((HINSTANCE) plugin->handle); | |||||
#else | #else | ||||
if (plugin->handle) | if (plugin->handle) | ||||
dlclose(plugin->handle); | dlclose(plugin->handle); | ||||
@@ -364,11 +340,40 @@ void pluginDestroy() { | |||||
// It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins. | // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins. | ||||
// delete plugin; | // delete plugin; | ||||
} | } | ||||
gPlugins.clear(); | |||||
plugins.clear(); | |||||
} | |||||
void PluginManager::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())); | |||||
json_t *resJ = network::requestJson(network::METHOD_POST, API_HOST + "/token", reqJ); | |||||
json_decref(reqJ); | |||||
if (resJ) { | |||||
json_t *errorJ = json_object_get(resJ, "error"); | |||||
if (errorJ) { | |||||
const char *errorStr = json_string_value(errorJ); | |||||
loginStatus = errorStr; | |||||
} | |||||
else { | |||||
json_t *tokenJ = json_object_get(resJ, "token"); | |||||
if (tokenJ) { | |||||
const char *tokenStr = json_string_value(tokenJ); | |||||
token = tokenStr; | |||||
loginStatus = ""; | |||||
} | |||||
} | |||||
json_decref(resJ); | |||||
} | |||||
} | |||||
void PluginManager::logOut() { | |||||
token = ""; | |||||
} | } | ||||
bool pluginSync(bool dryRun) { | |||||
if (gToken.empty()) | |||||
bool PluginManager::sync(bool dryRun) { | |||||
if (token.empty()) | |||||
return false; | return false; | ||||
bool available = false; | bool available = false; | ||||
@@ -384,7 +389,7 @@ bool pluginSync(bool dryRun) { | |||||
// Get user's plugins list | // Get user's plugins list | ||||
json_t *pluginsReqJ = json_object(); | json_t *pluginsReqJ = json_object(); | ||||
json_object_set(pluginsReqJ, "token", json_string(gToken.c_str())); | |||||
json_object_set(pluginsReqJ, "token", json_string(token.c_str())); | |||||
json_t *pluginsResJ = network::requestJson(network::METHOD_GET, API_HOST + "/plugins", pluginsReqJ); | json_t *pluginsResJ = network::requestJson(network::METHOD_GET, API_HOST + "/plugins", pluginsReqJ); | ||||
json_decref(pluginsReqJ); | json_decref(pluginsReqJ); | ||||
if (!pluginsResJ) { | if (!pluginsResJ) { | ||||
@@ -438,7 +443,7 @@ bool pluginSync(bool dryRun) { | |||||
if (!manifestJ) | if (!manifestJ) | ||||
continue; | continue; | ||||
if (syncPlugin(slug, manifestJ, dryRun)) { | |||||
if (PluginManager_syncPlugin(this, slug, manifestJ, dryRun)) { | |||||
available = true; | available = true; | ||||
} | } | ||||
} | } | ||||
@@ -446,61 +451,16 @@ bool pluginSync(bool dryRun) { | |||||
return available; | return available; | ||||
} | } | ||||
void pluginLogIn(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())); | |||||
json_t *resJ = network::requestJson(network::METHOD_POST, API_HOST + "/token", reqJ); | |||||
json_decref(reqJ); | |||||
if (resJ) { | |||||
json_t *errorJ = json_object_get(resJ, "error"); | |||||
if (errorJ) { | |||||
const char *errorStr = json_string_value(errorJ); | |||||
loginStatus = errorStr; | |||||
} | |||||
else { | |||||
json_t *tokenJ = json_object_get(resJ, "token"); | |||||
if (tokenJ) { | |||||
const char *tokenStr = json_string_value(tokenJ); | |||||
gToken = tokenStr; | |||||
loginStatus = ""; | |||||
} | |||||
} | |||||
json_decref(resJ); | |||||
} | |||||
} | |||||
void pluginLogOut() { | |||||
gToken = ""; | |||||
} | |||||
void pluginCancelDownload() { | |||||
void PluginManager::cancelDownload() { | |||||
// TODO | // TODO | ||||
} | } | ||||
bool pluginIsLoggedIn() { | |||||
return gToken != ""; | |||||
} | |||||
bool pluginIsDownloading() { | |||||
return isDownloading; | |||||
bool PluginManager::isLoggedIn() { | |||||
return token != ""; | |||||
} | } | ||||
float pluginGetDownloadProgress() { | |||||
return downloadProgress; | |||||
} | |||||
std::string pluginGetDownloadName() { | |||||
return downloadName; | |||||
} | |||||
std::string pluginGetLoginStatus() { | |||||
return loginStatus; | |||||
} | |||||
Plugin *pluginGetPlugin(std::string pluginSlug) { | |||||
for (Plugin *plugin : gPlugins) { | |||||
Plugin *PluginManager::getPlugin(std::string pluginSlug) { | |||||
for (Plugin *plugin : plugins) { | |||||
if (plugin->slug == pluginSlug) { | if (plugin->slug == pluginSlug) { | ||||
return plugin; | return plugin; | ||||
} | } | ||||
@@ -508,8 +468,8 @@ Plugin *pluginGetPlugin(std::string pluginSlug) { | |||||
return NULL; | return NULL; | ||||
} | } | ||||
Model *pluginGetModel(std::string pluginSlug, std::string modelSlug) { | |||||
Plugin *plugin = pluginGetPlugin(pluginSlug); | |||||
Model *PluginManager::getModel(std::string pluginSlug, std::string modelSlug) { | |||||
Plugin *plugin = getPlugin(pluginSlug); | |||||
if (plugin) { | if (plugin) { | ||||
for (Model *model : plugin->models) { | for (Model *model : plugin->models) { | ||||
if (model->slug == modelSlug) { | if (model->slug == modelSlug) { | ||||
@@ -521,4 +481,7 @@ Model *pluginGetModel(std::string pluginSlug, std::string modelSlug) { | |||||
} | } | ||||
PluginManager *gPluginManager = NULL; | |||||
} // namespace rack | } // namespace rack |
@@ -1,7 +1,7 @@ | |||||
#include "settings.hpp" | #include "settings.hpp" | ||||
#include "logger.hpp" | #include "logger.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "plugin.hpp" | |||||
#include "plugin/PluginManager.hpp" | |||||
#include "app/Scene.hpp" | #include "app/Scene.hpp" | ||||
#include "app/ModuleBrowser.hpp" | #include "app/ModuleBrowser.hpp" | ||||
#include "engine/Engine.hpp" | #include "engine/Engine.hpp" | ||||
@@ -20,7 +20,7 @@ static json_t *settingsToJson() { | |||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
// token | // token | ||||
json_t *tokenJ = json_string(gToken.c_str()); | |||||
json_t *tokenJ = json_string(gPluginManager->token.c_str()); | |||||
json_object_set_new(rootJ, "token", tokenJ); | json_object_set_new(rootJ, "token", tokenJ); | ||||
if (!windowIsMaximized()) { | if (!windowIsMaximized()) { | ||||
@@ -83,7 +83,7 @@ static void settingsFromJson(json_t *rootJ) { | |||||
// token | // token | ||||
json_t *tokenJ = json_object_get(rootJ, "token"); | json_t *tokenJ = json_object_get(rootJ, "token"); | ||||
if (tokenJ) | if (tokenJ) | ||||
gToken = json_string_value(tokenJ); | |||||
gPluginManager->token = json_string_value(tokenJ); | |||||
// windowSize | // windowSize | ||||
json_t *windowSizeJ = json_object_get(rootJ, "windowSize"); | json_t *windowSizeJ = json_object_get(rootJ, "windowSize"); | ||||