Browse Source

Renamed Plugin to Manufacturer, added multiline to TextField, added

Notes to Core, changed plugin build system to support multiple targets
per manufacturer
tags/v0.5.0
Andrew Belt 7 years ago
parent
commit
0c4cf64726
17 changed files with 143 additions and 103 deletions
  1. +1
    -1
      README.md
  2. +9
    -0
      compile.mk
  3. +2
    -2
      include/asset.hpp
  4. +20
    -15
      include/plugin.hpp
  5. +1
    -6
      include/rack.hpp
  6. +1
    -0
      include/widgets.hpp
  7. +4
    -6
      plugin.mk
  8. +2
    -2
      src/app/ModuleWidget.cpp
  9. +3
    -2
      src/app/RackScene.cpp
  10. +22
    -22
      src/app/RackWidget.cpp
  11. +3
    -3
      src/asset.cpp
  12. +1
    -1
      src/core/MidiClockToCV.cpp
  13. +12
    -11
      src/core/core.cpp
  14. +7
    -0
      src/core/core.hpp
  15. +32
    -32
      src/plugin.cpp
  16. +15
    -0
      src/widgets/FramebufferWidget.cpp
  17. +8
    -0
      src/widgets/TextField.cpp

+ 1
- 1
README.md View File

@@ -9,7 +9,7 @@ This README includes instructions for building Rack from source. For information
## The [Issue Tracker](https://github.com/VCVRack/Rack/issues) *is* the official developer's forum

Bug reports, feature requests, and even *questions/discussions* are welcome on the GitHub Issue Tracker for all VCVRack repos.
However, please search before posting to avoid duplicates.
However, please search before posting to avoid duplicates, and limit to one issue per post.

You may vote on feature requests by using the Thumbs Up/Down reaction on the first post.



+ 9
- 0
compile.mk View File

@@ -42,6 +42,15 @@ DEPS = $(patsubst %, build/%.d, $(SOURCES))
$(TARGET): $(OBJECTS)
$(CXX) -o $@ $^ $(LDFLAGS)

%.so:
$(CXX) -o $@ $^ $(LDFLAGS)

%.dylib:
$(CXX) -o $@ $^ $(LDFLAGS)

%.dll:
$(CXX) -o $@ $^ $(LDFLAGS)

# Object targets

-include $(DEPS)


+ 2
- 2
include/asset.hpp View File

@@ -13,9 +13,9 @@ std::string assetGlobal(std::string filename);
/** Searches for a local resource
*/
std::string assetLocal(std::string filename);
/** Searches for a plugin resource, given a Plugin object
/** Searches for a manufacturer resource, given a Manufacturer object
*/
std::string assetPlugin(Plugin *plugin, std::string filename);
std::string assetManufacturer(Manufacturer *manufacturer, std::string filename);


} // namespace rack

+ 20
- 15
include/plugin.hpp View File

@@ -10,37 +10,41 @@ struct ModuleWidget;
struct Model;

// Subclass this and return a pointer to a new one when init() is called
struct Plugin {
virtual ~Plugin();

/** A unique identifier for your plugin, e.g. "foo" */
std::string slug;
/** Human readable name for your plugin, e.g. "Foo Modular" */
std::string name;
/** A list of the models made available by this plugin */
struct Manufacturer {
/** A list of the models available by this manufacturer, add with addModel() */
std::list<Model*> models;
/** The file path of the plugins directory */
std::string path;
/** OS-dependent library handle */
void *handle = NULL;

// You may set everything below this point in your plugin

/** A unique identifier for the manufacturer, e.g. "foo" */
std::string slug;
/** Human readable name for the manufacturer, e.g. "Foo Modular" */
std::string name;
/** Optional metadata for the Add Module context menu */
std::string homepageUrl;
std::string manualUrl;
std::string version;

virtual ~Manufacturer();
void addModel(Model *model);
};

struct Model {
virtual ~Model() {}

Plugin *plugin;
/** A unique identifier for the model in this plugin, e.g. "VCO" */
Manufacturer *manufacturer = NULL;
/** A unique identifier for the model, e.g. "VCO" */
std::string slug;
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */
std::string name;

virtual ~Model() {}
virtual ModuleWidget *createModuleWidget() { return NULL; }
};

extern std::list<Plugin*> gPlugins;
extern std::list<Manufacturer*> gManufacturers;
extern std::string gToken;

void pluginInit();
@@ -62,7 +66,8 @@ std::string pluginGetLoginStatus();
// Implemented by plugin
////////////////////

/** Called once to initialize and return the Plugin instance.
/** Called once to initialize and return the Manufacturer instance.
You must implement this in your plugin
*/
extern "C"
void init(rack::Plugin *plugin);
void init(rack::Manufacturer *manufacturer);

+ 1
- 6
include/rack.hpp View File

@@ -18,7 +18,7 @@ namespace rack {
////////////////////

template <class TModuleWidget>
Model *createModel(Plugin *plugin, std::string slug, std::string name) {
Model *createModel(std::string slug, std::string name) {
struct TModel : Model {
ModuleWidget *createModuleWidget() override {
ModuleWidget *moduleWidget = new TModuleWidget();
@@ -27,13 +27,8 @@ Model *createModel(Plugin *plugin, std::string slug, std::string name) {
}
};
Model *model = new TModel();
model->plugin = plugin;
model->slug = slug;
model->name = name;
// Create bi-directional association between the Plugin and Model
if (plugin) {
plugin->models.push_back(model);
}
return model;
}



+ 1
- 0
include/widgets.hpp View File

@@ -399,6 +399,7 @@ struct ZoomWidget : Widget {
struct TextField : OpaqueWidget {
std::string text;
std::string placeholder;
bool multiline = false;
int begin = 0;
int end = 0;



+ 4
- 6
plugin.mk View File

@@ -8,23 +8,21 @@ include ../../arch.mk

ifeq ($(ARCH), lin)
LDFLAGS += -shared
TARGET = plugin.so
PLUGIN_EXTENSION = so
endif

ifeq ($(ARCH), mac)
LDFLAGS += -shared -undefined dynamic_lookup
TARGET = plugin.dylib
PLUGIN_EXTENSION = dylib
endif

ifeq ($(ARCH), win)
LDFLAGS += -shared -L../../ -lRack
TARGET = plugin.dll
PLUGIN_EXTENSION = dll
endif


all: $(TARGET)

clean:
rm -rfv build $(TARGET) dist
rm -rfv build *.$(PLUGIN_EXTENSION) dist

include ../../compile.mk

+ 2
- 2
src/app/ModuleWidget.cpp View File

@@ -46,7 +46,7 @@ json_t *ModuleWidget::toJson() {
json_t *rootJ = json_object();

// plugin
json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str()));
json_object_set_new(rootJ, "plugin", json_string(model->manufacturer->slug.c_str()));
// model
json_object_set_new(rootJ, "model", json_string(model->slug.c_str()));
// pos
@@ -245,7 +245,7 @@ Menu *ModuleWidget::createContextMenu() {
Menu *menu = gScene->createMenu();

MenuLabel *menuLabel = new MenuLabel();
menuLabel->text = model->plugin->name + ": " + model->name;
menuLabel->text = model->manufacturer->name + ": " + model->name;
menu->pushChild(menuLabel);

ResetMenuItem *resetItem = new ResetMenuItem();


+ 3
- 2
src/app/RackScene.cpp View File

@@ -2,6 +2,7 @@
#include "gui.hpp"
#include "util/request.hpp"
#include "../ext/osdialog/osdialog.h"
#include <string.h>
#include <thread>


@@ -17,7 +18,7 @@ static void checkVersion() {
json_t *versionJ = json_object_get(resJ, "version");
if (versionJ) {
const char *version = json_string_value(versionJ);
if (version && version != gApplicationVersion) {
if (version && strlen(version) > 0 && version != gApplicationVersion) {
newVersion = version;
}
}
@@ -45,7 +46,7 @@ RackScene::RackScene() {
scrollWidget->box.pos.y = gToolbar->box.size.y;

// Check for new version
if (gApplicationVersion != "dev") {
if (!gApplicationVersion.empty()) {
std::thread versionThread(checkVersion);
versionThread.detach();
}


+ 22
- 22
src/app/RackWidget.cpp View File

@@ -208,21 +208,21 @@ void RackWidget::fromJson(json_t *rootJ) {
const char *modelSlug = json_string_value(modelSlugJ);

// Search for plugin
Plugin *plugin = NULL;
for (Plugin *p : gPlugins) {
if (p->slug == pluginSlug) {
plugin = p;
Manufacturer *manufacturer = NULL;
for (Manufacturer *m : gManufacturers) {
if (m->slug == pluginSlug) {
manufacturer = m;
break;
}
}
if (!plugin) {
if (!manufacturer) {
message += stringf("Could not find plugin \"%s\" for module \"%s\".\n", pluginSlug, modelSlug);
continue;
}

// Get for model
Model *model = NULL;
for (Model *m : plugin->models) {
for (Model *m : manufacturer->models) {
if (m->slug == modelSlug) {
model = m;
break;
@@ -388,13 +388,13 @@ struct UrlItem : MenuItem {
}
};

struct AddPluginMenuItem : MenuItem {
Plugin *plugin;
struct AddManufacturerMenuItem : MenuItem {
Manufacturer *manufacturer;
Vec modulePos;
Menu *createChildMenu() override {
// Model items
Menu *menu = new Menu();
for (Model *model : plugin->models) {
for (Model *model : manufacturer->models) {
AddModuleMenuItem *item = new AddModuleMenuItem();
item->text = model->name;
item->model = model;
@@ -409,34 +409,34 @@ struct AddPluginMenuItem : MenuItem {
}
{
MenuLabel *label = new MenuLabel();
label->text = plugin->name;
label->text = manufacturer->name;
menu->pushChild(label);
}

if (!plugin->homepageUrl.empty()) {
if (!manufacturer->homepageUrl.empty()) {
UrlItem *item = new UrlItem();
item->text = "Homepage";
item->url = plugin->homepageUrl;
item->url = manufacturer->homepageUrl;
menu->pushChild(item);
}

if (!plugin->manualUrl.empty()) {
if (!manufacturer->manualUrl.empty()) {
UrlItem *item = new UrlItem();
item->text = "Manual";
item->url = plugin->manualUrl;
item->url = manufacturer->manualUrl;
menu->pushChild(item);
}

if (!plugin->path.empty()) {
if (!manufacturer->path.empty()) {
UrlItem *item = new UrlItem();
item->text = "Browse directory";
item->url = plugin->path;
item->url = manufacturer->path;
menu->pushChild(item);
}

if (!plugin->version.empty()) {
if (!manufacturer->version.empty()) {
MenuLabel *item = new MenuLabel();
item->text = "Version: v" + plugin->version;
item->text = "Version: v" + manufacturer->version;
menu->pushChild(item);
}

@@ -452,10 +452,10 @@ void RackWidget::onMouseDownOpaque(int button) {
MenuLabel *menuLabel = new MenuLabel();
menuLabel->text = "Add module";
menu->pushChild(menuLabel);
for (Plugin *plugin : gPlugins) {
AddPluginMenuItem *item = new AddPluginMenuItem();
item->text = plugin->name;
item->plugin = plugin;
for (Manufacturer *manufacturer : gManufacturers) {
AddManufacturerMenuItem *item = new AddManufacturerMenuItem();
item->text = manufacturer->name;
item->manufacturer = manufacturer;
item->modulePos = modulePos;
menu->pushChild(item);
}


+ 3
- 3
src/asset.cpp View File

@@ -88,10 +88,10 @@ std::string assetLocal(std::string filename) {
return path;
}

std::string assetPlugin(Plugin *plugin, std::string filename) {
assert(plugin);
std::string assetManufacturer(Manufacturer *manufacturer, std::string filename) {
assert(manufacturer);
std::string path;
path = plugin->path + "/" + filename;
path = manufacturer->path + "/" + filename;
return path;
}



+ 1
- 1
src/core/MidiClockToCV.cpp View File

@@ -241,7 +241,7 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() {
{
Label *label = new Label();
label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
label->text = "MIDI Clock to CV";
label->text = "MIDI Clk-CV";
addChild(label);
yPos = labelHeight * 2;
}


+ 12
- 11
src/core/core.cpp View File

@@ -2,15 +2,16 @@
#include "MidiIO.hpp"


void init(rack::Plugin *plugin) {
plugin->slug = "Core";
plugin->name = "Core";
plugin->homepageUrl = "https://vcvrack.com/";
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface");
createModel<MidiToCVWidget>(plugin, "MIDIToCVInterface", "MIDI-to-CV Interface");
createModel<MIDICCToCVWidget>(plugin, "MIDICCToCVInterface", "MIDI CC-to-CV Interface");
createModel<MIDIClockToCVWidget>(plugin, "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface");
createModel<MIDITriggerToCVWidget>(plugin, "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface");
// createModel<BridgeWidget>(plugin, "Bridge", "Bridge");
createModel<BlankWidget>(plugin, "Blank", "Blank");
void init(rack::Manufacturer *m) {
m->slug = "Core";
m->name = "Core";
m->homepageUrl = "https://vcvrack.com/";
m->addModel(createModel<AudioInterfaceWidget>("AudioInterface", "Audio Interface"));
m->addModel(createModel<MidiToCVWidget>("MIDIToCVInterface", "MIDI-to-CV Interface"));
m->addModel(createModel<MIDICCToCVWidget>("MIDICCToCVInterface", "MIDI CC-to-CV Interface"));
m->addModel(createModel<MIDIClockToCVWidget>("MIDIClockToCVInterface", "MIDI Clock-to-CV Interface"));
m->addModel(createModel<MIDITriggerToCVWidget>("MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface"));
// m->addModel(createModel<BridgeWidget>("Bridge", "Bridge"));
m->addModel(createModel<BlankWidget>("Blank", "Blank"));
m->addModel(createModel<NotesWidget>("Notes", "Notes"));
}

+ 7
- 0
src/core/core.hpp View File

@@ -26,3 +26,10 @@ struct BlankWidget : ModuleWidget {
json_t *toJson() override;
void fromJson(json_t *rootJ) override;
};

struct NotesWidget : ModuleWidget {
TextField *textField;
NotesWidget();
json_t *toJson() override;
void fromJson(json_t *rootJ) override;
};

+ 32
- 32
src/plugin.cpp View File

@@ -28,7 +28,7 @@

namespace rack {

std::list<Plugin*> gPlugins;
std::list<Manufacturer*> gManufacturers;
std::string gToken;

static bool isDownloading = false;
@@ -38,12 +38,19 @@ static std::string loginStatus;



Plugin::~Plugin() {
Manufacturer::~Manufacturer() {
for (Model *model : models) {
delete model;
}
}

void Manufacturer::addModel(Model *model) {
assert(!model->manufacturer);
model->manufacturer = this;
models.push_back(model);
}


static int loadPlugin(std::string path) {
std::string libraryFilename;
#if ARCH_LIN
@@ -70,8 +77,8 @@ static int loadPlugin(std::string path) {
}
#endif

// Call plugin init() function
typedef void (*InitCallback)(Plugin *);
// Call plugin's init() function
typedef void (*InitCallback)(Manufacturer *);
InitCallback initCallback;
#if ARCH_WIN
initCallback = (InitCallback) GetProcAddress(handle, "init");
@@ -83,27 +90,23 @@ static int loadPlugin(std::string path) {
return -2;
}

// Construct and initialize Plugin instance
Plugin *plugin = new Plugin();
plugin->path = path;
plugin->handle = handle;
initCallback(plugin);
// Construct and initialize Manufacturer instance
Manufacturer *manufacturer = new Manufacturer();
manufacturer->path = path;
manufacturer->handle = handle;
initCallback(manufacturer);

// Check that this is a unique slug
for (Plugin *otherPlugin : gPlugins) {
assert(plugin->slug != otherPlugin->slug);
for (Manufacturer *otherManufacturer : gManufacturers) {
assert(manufacturer->slug != otherManufacturer->slug);
}

// Add plugin to list
gPlugins.push_back(plugin);
// Add manufacturer to list
gManufacturers.push_back(manufacturer);
fprintf(stderr, "Loaded plugin %s\n", libraryFilename.c_str());
return 0;
}

static bool comparePlugins(Plugin *a, Plugin *b) {
return a->slug < b->slug;
}

static void loadPlugins(std::string path) {
DIR *dir = opendir(path.c_str());
if (dir) {
@@ -115,9 +118,6 @@ static void loadPlugins(std::string path) {
}
closedir(dir);
}

// Sort plugins
gPlugins.sort(comparePlugins);
}

////////////////////
@@ -201,8 +201,8 @@ static void refreshPurchase(json_t *pluginJ) {
url += gToken;

// Find slug in plugins list, and return silently if slug already exists
for (Plugin *p : gPlugins) {
if (p->slug == slug) {
for (Manufacturer *m : gManufacturers) {
if (m->slug == slug) {
return;
}
}
@@ -237,9 +237,9 @@ static void refreshPurchase(json_t *pluginJ) {
void pluginInit() {
// Load core
// This function is defined in core.cpp
Plugin *corePlugin = new Plugin();
init(corePlugin);
gPlugins.push_back(corePlugin);
Manufacturer *coreManufacturer = new Manufacturer();
init(coreManufacturer);
gManufacturers.push_back(coreManufacturer);

// Load plugins from global directory
std::string globalPlugins = assetGlobal("plugins");
@@ -255,20 +255,20 @@ void pluginInit() {
}

void pluginDestroy() {
for (Plugin *plugin : gPlugins) {
for (Manufacturer *manufacturer : gManufacturers) {
// Free library handle
#if ARCH_WIN
if (plugin->handle)
FreeLibrary((HINSTANCE)plugin->handle);
if (manufacturer->handle)
FreeLibrary((HINSTANCE)manufacturer->handle);
#elif ARCH_LIN || ARCH_MAC
if (plugin->handle)
dlclose(plugin->handle);
if (manufacturer->handle)
dlclose(manufacturer->handle);
#endif

// For some reason this segfaults
// delete plugin;
// delete manufacturer;
}
gPlugins.clear();
gManufacturers.clear();
}

void pluginLogIn(std::string email, std::string password) {


+ 15
- 0
src/widgets/FramebufferWidget.cpp View File

@@ -42,6 +42,7 @@ void FramebufferWidget::step() {
internal->box.size = box.size;
internal->box.size = Vec(ceilf(internal->box.size.x), ceilf(internal->box.size.y));
Vec fbSize = internal->box.size.mult(gPixelRatio * oversample);

// assert(fbSize.isFinite());
// Reject zero area size
if (fbSize.x <= 0.0 || fbSize.y <= 0.0)
@@ -71,6 +72,20 @@ void FramebufferWidget::step() {
}

void FramebufferWidget::draw(NVGcontext *vg) {
// {
// float xform[6];
// nvgCurrentTransform(vg, xform);
// printf("%f %f %f %f %f %f\n", xform[0], xform[1], xform[2], xform[3], xform[4], xform[5]);
// nvgSave(vg);
// nvgResetTransform(vg);
// nvgTranslate(vg, xform[5], xform[6]);
// nvgBeginPath(vg);
// nvgRect(vg, 0, 0, 50, 50);
// nvgFillColor(vg, nvgRGBf(1.0, 0.0, 0.0));
// nvgFill(vg);
// nvgRestore(vg);
// }

if (!internal->fb) {
// Bypass framebuffer cache entirely
// Widget::draw(vg);


+ 8
- 0
src/widgets/TextField.cpp View File

@@ -101,6 +101,14 @@ bool TextField::onFocusKey(int key) {
}
}
break;
case GLFW_KEY_ENTER:
if (multiline) {
insertText("\n");
}
else {
onAction();
}
break;
}

begin = mini(maxi(begin, 0), text.size());


Loading…
Cancel
Save