Notes to Core, changed plugin build system to support multiple targets per manufacturertags/v0.5.0
@@ -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 | ## 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. | 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. | You may vote on feature requests by using the Thumbs Up/Down reaction on the first post. | ||||
@@ -42,6 +42,15 @@ DEPS = $(patsubst %, build/%.d, $(SOURCES)) | |||||
$(TARGET): $(OBJECTS) | $(TARGET): $(OBJECTS) | ||||
$(CXX) -o $@ $^ $(LDFLAGS) | $(CXX) -o $@ $^ $(LDFLAGS) | ||||
%.so: | |||||
$(CXX) -o $@ $^ $(LDFLAGS) | |||||
%.dylib: | |||||
$(CXX) -o $@ $^ $(LDFLAGS) | |||||
%.dll: | |||||
$(CXX) -o $@ $^ $(LDFLAGS) | |||||
# Object targets | # Object targets | ||||
-include $(DEPS) | -include $(DEPS) | ||||
@@ -13,9 +13,9 @@ std::string assetGlobal(std::string filename); | |||||
/** Searches for a local resource | /** Searches for a local resource | ||||
*/ | */ | ||||
std::string assetLocal(std::string filename); | 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 | } // namespace rack |
@@ -10,37 +10,41 @@ struct ModuleWidget; | |||||
struct Model; | struct Model; | ||||
// Subclass this and return a pointer to a new one when init() is called | // 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; | std::list<Model*> models; | ||||
/** The file path of the plugins directory */ | /** The file path of the plugins directory */ | ||||
std::string path; | std::string path; | ||||
/** OS-dependent library handle */ | /** OS-dependent library handle */ | ||||
void *handle = NULL; | 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 */ | /** Optional metadata for the Add Module context menu */ | ||||
std::string homepageUrl; | std::string homepageUrl; | ||||
std::string manualUrl; | std::string manualUrl; | ||||
std::string version; | std::string version; | ||||
virtual ~Manufacturer(); | |||||
void addModel(Model *model); | |||||
}; | }; | ||||
struct 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; | std::string slug; | ||||
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | /** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | ||||
std::string name; | std::string name; | ||||
virtual ~Model() {} | |||||
virtual ModuleWidget *createModuleWidget() { return NULL; } | virtual ModuleWidget *createModuleWidget() { return NULL; } | ||||
}; | }; | ||||
extern std::list<Plugin*> gPlugins; | |||||
extern std::list<Manufacturer*> gManufacturers; | |||||
extern std::string gToken; | extern std::string gToken; | ||||
void pluginInit(); | void pluginInit(); | ||||
@@ -62,7 +66,8 @@ std::string pluginGetLoginStatus(); | |||||
// Implemented by plugin | // 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" | extern "C" | ||||
void init(rack::Plugin *plugin); | |||||
void init(rack::Manufacturer *manufacturer); |
@@ -18,7 +18,7 @@ namespace rack { | |||||
//////////////////// | //////////////////// | ||||
template <class TModuleWidget> | template <class TModuleWidget> | ||||
Model *createModel(Plugin *plugin, std::string slug, std::string name) { | |||||
Model *createModel(std::string slug, std::string name) { | |||||
struct TModel : Model { | struct TModel : Model { | ||||
ModuleWidget *createModuleWidget() override { | ModuleWidget *createModuleWidget() override { | ||||
ModuleWidget *moduleWidget = new TModuleWidget(); | ModuleWidget *moduleWidget = new TModuleWidget(); | ||||
@@ -27,13 +27,8 @@ Model *createModel(Plugin *plugin, std::string slug, std::string name) { | |||||
} | } | ||||
}; | }; | ||||
Model *model = new TModel(); | Model *model = new TModel(); | ||||
model->plugin = plugin; | |||||
model->slug = slug; | model->slug = slug; | ||||
model->name = name; | model->name = name; | ||||
// Create bi-directional association between the Plugin and Model | |||||
if (plugin) { | |||||
plugin->models.push_back(model); | |||||
} | |||||
return model; | return model; | ||||
} | } | ||||
@@ -399,6 +399,7 @@ struct ZoomWidget : Widget { | |||||
struct TextField : OpaqueWidget { | struct TextField : OpaqueWidget { | ||||
std::string text; | std::string text; | ||||
std::string placeholder; | std::string placeholder; | ||||
bool multiline = false; | |||||
int begin = 0; | int begin = 0; | ||||
int end = 0; | int end = 0; | ||||
@@ -8,23 +8,21 @@ include ../../arch.mk | |||||
ifeq ($(ARCH), lin) | ifeq ($(ARCH), lin) | ||||
LDFLAGS += -shared | LDFLAGS += -shared | ||||
TARGET = plugin.so | |||||
PLUGIN_EXTENSION = so | |||||
endif | endif | ||||
ifeq ($(ARCH), mac) | ifeq ($(ARCH), mac) | ||||
LDFLAGS += -shared -undefined dynamic_lookup | LDFLAGS += -shared -undefined dynamic_lookup | ||||
TARGET = plugin.dylib | |||||
PLUGIN_EXTENSION = dylib | |||||
endif | endif | ||||
ifeq ($(ARCH), win) | ifeq ($(ARCH), win) | ||||
LDFLAGS += -shared -L../../ -lRack | LDFLAGS += -shared -L../../ -lRack | ||||
TARGET = plugin.dll | |||||
PLUGIN_EXTENSION = dll | |||||
endif | endif | ||||
all: $(TARGET) | |||||
clean: | clean: | ||||
rm -rfv build $(TARGET) dist | |||||
rm -rfv build *.$(PLUGIN_EXTENSION) dist | |||||
include ../../compile.mk | include ../../compile.mk |
@@ -46,7 +46,7 @@ json_t *ModuleWidget::toJson() { | |||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
// plugin | // 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 | // model | ||||
json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); | ||||
// pos | // pos | ||||
@@ -245,7 +245,7 @@ Menu *ModuleWidget::createContextMenu() { | |||||
Menu *menu = gScene->createMenu(); | Menu *menu = gScene->createMenu(); | ||||
MenuLabel *menuLabel = new MenuLabel(); | MenuLabel *menuLabel = new MenuLabel(); | ||||
menuLabel->text = model->plugin->name + ": " + model->name; | |||||
menuLabel->text = model->manufacturer->name + ": " + model->name; | |||||
menu->pushChild(menuLabel); | menu->pushChild(menuLabel); | ||||
ResetMenuItem *resetItem = new ResetMenuItem(); | ResetMenuItem *resetItem = new ResetMenuItem(); | ||||
@@ -2,6 +2,7 @@ | |||||
#include "gui.hpp" | #include "gui.hpp" | ||||
#include "util/request.hpp" | #include "util/request.hpp" | ||||
#include "../ext/osdialog/osdialog.h" | #include "../ext/osdialog/osdialog.h" | ||||
#include <string.h> | |||||
#include <thread> | #include <thread> | ||||
@@ -17,7 +18,7 @@ static void checkVersion() { | |||||
json_t *versionJ = json_object_get(resJ, "version"); | json_t *versionJ = json_object_get(resJ, "version"); | ||||
if (versionJ) { | if (versionJ) { | ||||
const char *version = json_string_value(versionJ); | const char *version = json_string_value(versionJ); | ||||
if (version && version != gApplicationVersion) { | |||||
if (version && strlen(version) > 0 && version != gApplicationVersion) { | |||||
newVersion = version; | newVersion = version; | ||||
} | } | ||||
} | } | ||||
@@ -45,7 +46,7 @@ RackScene::RackScene() { | |||||
scrollWidget->box.pos.y = gToolbar->box.size.y; | scrollWidget->box.pos.y = gToolbar->box.size.y; | ||||
// Check for new version | // Check for new version | ||||
if (gApplicationVersion != "dev") { | |||||
if (!gApplicationVersion.empty()) { | |||||
std::thread versionThread(checkVersion); | std::thread versionThread(checkVersion); | ||||
versionThread.detach(); | versionThread.detach(); | ||||
} | } | ||||
@@ -208,21 +208,21 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
const char *modelSlug = json_string_value(modelSlugJ); | const char *modelSlug = json_string_value(modelSlugJ); | ||||
// Search for plugin | // 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; | break; | ||||
} | } | ||||
} | } | ||||
if (!plugin) { | |||||
if (!manufacturer) { | |||||
message += stringf("Could not find plugin \"%s\" for module \"%s\".\n", pluginSlug, modelSlug); | message += stringf("Could not find plugin \"%s\" for module \"%s\".\n", pluginSlug, modelSlug); | ||||
continue; | continue; | ||||
} | } | ||||
// Get for model | // Get for model | ||||
Model *model = NULL; | Model *model = NULL; | ||||
for (Model *m : plugin->models) { | |||||
for (Model *m : manufacturer->models) { | |||||
if (m->slug == modelSlug) { | if (m->slug == modelSlug) { | ||||
model = m; | model = m; | ||||
break; | break; | ||||
@@ -388,13 +388,13 @@ struct UrlItem : MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct AddPluginMenuItem : MenuItem { | |||||
Plugin *plugin; | |||||
struct AddManufacturerMenuItem : MenuItem { | |||||
Manufacturer *manufacturer; | |||||
Vec modulePos; | Vec modulePos; | ||||
Menu *createChildMenu() override { | Menu *createChildMenu() override { | ||||
// Model items | // Model items | ||||
Menu *menu = new Menu(); | Menu *menu = new Menu(); | ||||
for (Model *model : plugin->models) { | |||||
for (Model *model : manufacturer->models) { | |||||
AddModuleMenuItem *item = new AddModuleMenuItem(); | AddModuleMenuItem *item = new AddModuleMenuItem(); | ||||
item->text = model->name; | item->text = model->name; | ||||
item->model = model; | item->model = model; | ||||
@@ -409,34 +409,34 @@ struct AddPluginMenuItem : MenuItem { | |||||
} | } | ||||
{ | { | ||||
MenuLabel *label = new MenuLabel(); | MenuLabel *label = new MenuLabel(); | ||||
label->text = plugin->name; | |||||
label->text = manufacturer->name; | |||||
menu->pushChild(label); | menu->pushChild(label); | ||||
} | } | ||||
if (!plugin->homepageUrl.empty()) { | |||||
if (!manufacturer->homepageUrl.empty()) { | |||||
UrlItem *item = new UrlItem(); | UrlItem *item = new UrlItem(); | ||||
item->text = "Homepage"; | item->text = "Homepage"; | ||||
item->url = plugin->homepageUrl; | |||||
item->url = manufacturer->homepageUrl; | |||||
menu->pushChild(item); | menu->pushChild(item); | ||||
} | } | ||||
if (!plugin->manualUrl.empty()) { | |||||
if (!manufacturer->manualUrl.empty()) { | |||||
UrlItem *item = new UrlItem(); | UrlItem *item = new UrlItem(); | ||||
item->text = "Manual"; | item->text = "Manual"; | ||||
item->url = plugin->manualUrl; | |||||
item->url = manufacturer->manualUrl; | |||||
menu->pushChild(item); | menu->pushChild(item); | ||||
} | } | ||||
if (!plugin->path.empty()) { | |||||
if (!manufacturer->path.empty()) { | |||||
UrlItem *item = new UrlItem(); | UrlItem *item = new UrlItem(); | ||||
item->text = "Browse directory"; | item->text = "Browse directory"; | ||||
item->url = plugin->path; | |||||
item->url = manufacturer->path; | |||||
menu->pushChild(item); | menu->pushChild(item); | ||||
} | } | ||||
if (!plugin->version.empty()) { | |||||
if (!manufacturer->version.empty()) { | |||||
MenuLabel *item = new MenuLabel(); | MenuLabel *item = new MenuLabel(); | ||||
item->text = "Version: v" + plugin->version; | |||||
item->text = "Version: v" + manufacturer->version; | |||||
menu->pushChild(item); | menu->pushChild(item); | ||||
} | } | ||||
@@ -452,10 +452,10 @@ void RackWidget::onMouseDownOpaque(int button) { | |||||
MenuLabel *menuLabel = new MenuLabel(); | MenuLabel *menuLabel = new MenuLabel(); | ||||
menuLabel->text = "Add module"; | menuLabel->text = "Add module"; | ||||
menu->pushChild(menuLabel); | 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; | item->modulePos = modulePos; | ||||
menu->pushChild(item); | menu->pushChild(item); | ||||
} | } | ||||
@@ -88,10 +88,10 @@ std::string assetLocal(std::string filename) { | |||||
return path; | 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; | std::string path; | ||||
path = plugin->path + "/" + filename; | |||||
path = manufacturer->path + "/" + filename; | |||||
return path; | return path; | ||||
} | } | ||||
@@ -241,7 +241,7 @@ MIDIClockToCVWidget::MIDIClockToCVWidget() { | |||||
{ | { | ||||
Label *label = new Label(); | Label *label = new Label(); | ||||
label->box.pos = Vec(box.size.x - margin - 7 * 15, margin); | label->box.pos = Vec(box.size.x - margin - 7 * 15, margin); | ||||
label->text = "MIDI Clock to CV"; | |||||
label->text = "MIDI Clk-CV"; | |||||
addChild(label); | addChild(label); | ||||
yPos = labelHeight * 2; | yPos = labelHeight * 2; | ||||
} | } | ||||
@@ -2,15 +2,16 @@ | |||||
#include "MidiIO.hpp" | #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")); | |||||
} | } |
@@ -26,3 +26,10 @@ struct BlankWidget : ModuleWidget { | |||||
json_t *toJson() override; | json_t *toJson() override; | ||||
void fromJson(json_t *rootJ) override; | void fromJson(json_t *rootJ) override; | ||||
}; | }; | ||||
struct NotesWidget : ModuleWidget { | |||||
TextField *textField; | |||||
NotesWidget(); | |||||
json_t *toJson() override; | |||||
void fromJson(json_t *rootJ) override; | |||||
}; |
@@ -28,7 +28,7 @@ | |||||
namespace rack { | namespace rack { | ||||
std::list<Plugin*> gPlugins; | |||||
std::list<Manufacturer*> gManufacturers; | |||||
std::string gToken; | std::string gToken; | ||||
static bool isDownloading = false; | static bool isDownloading = false; | ||||
@@ -38,12 +38,19 @@ static std::string loginStatus; | |||||
Plugin::~Plugin() { | |||||
Manufacturer::~Manufacturer() { | |||||
for (Model *model : models) { | for (Model *model : models) { | ||||
delete model; | delete model; | ||||
} | } | ||||
} | } | ||||
void Manufacturer::addModel(Model *model) { | |||||
assert(!model->manufacturer); | |||||
model->manufacturer = this; | |||||
models.push_back(model); | |||||
} | |||||
static int loadPlugin(std::string path) { | static int loadPlugin(std::string path) { | ||||
std::string libraryFilename; | std::string libraryFilename; | ||||
#if ARCH_LIN | #if ARCH_LIN | ||||
@@ -70,8 +77,8 @@ static int loadPlugin(std::string path) { | |||||
} | } | ||||
#endif | #endif | ||||
// Call plugin init() function | |||||
typedef void (*InitCallback)(Plugin *); | |||||
// Call plugin's init() function | |||||
typedef void (*InitCallback)(Manufacturer *); | |||||
InitCallback initCallback; | InitCallback initCallback; | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
initCallback = (InitCallback) GetProcAddress(handle, "init"); | initCallback = (InitCallback) GetProcAddress(handle, "init"); | ||||
@@ -83,27 +90,23 @@ static int loadPlugin(std::string path) { | |||||
return -2; | 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 | // 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()); | fprintf(stderr, "Loaded plugin %s\n", libraryFilename.c_str()); | ||||
return 0; | return 0; | ||||
} | } | ||||
static bool comparePlugins(Plugin *a, Plugin *b) { | |||||
return a->slug < b->slug; | |||||
} | |||||
static void loadPlugins(std::string path) { | static void loadPlugins(std::string path) { | ||||
DIR *dir = opendir(path.c_str()); | DIR *dir = opendir(path.c_str()); | ||||
if (dir) { | if (dir) { | ||||
@@ -115,9 +118,6 @@ static void loadPlugins(std::string path) { | |||||
} | } | ||||
closedir(dir); | closedir(dir); | ||||
} | } | ||||
// Sort plugins | |||||
gPlugins.sort(comparePlugins); | |||||
} | } | ||||
//////////////////// | //////////////////// | ||||
@@ -201,8 +201,8 @@ static void refreshPurchase(json_t *pluginJ) { | |||||
url += gToken; | url += gToken; | ||||
// Find slug in plugins list, and return silently if slug already exists | // 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; | return; | ||||
} | } | ||||
} | } | ||||
@@ -237,9 +237,9 @@ static void refreshPurchase(json_t *pluginJ) { | |||||
void pluginInit() { | void pluginInit() { | ||||
// Load core | // Load core | ||||
// This function is defined in core.cpp | // 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 | // Load plugins from global directory | ||||
std::string globalPlugins = assetGlobal("plugins"); | std::string globalPlugins = assetGlobal("plugins"); | ||||
@@ -255,20 +255,20 @@ void pluginInit() { | |||||
} | } | ||||
void pluginDestroy() { | void pluginDestroy() { | ||||
for (Plugin *plugin : gPlugins) { | |||||
for (Manufacturer *manufacturer : gManufacturers) { | |||||
// Free library handle | // Free library handle | ||||
#if ARCH_WIN | #if ARCH_WIN | ||||
if (plugin->handle) | |||||
FreeLibrary((HINSTANCE)plugin->handle); | |||||
if (manufacturer->handle) | |||||
FreeLibrary((HINSTANCE)manufacturer->handle); | |||||
#elif ARCH_LIN || ARCH_MAC | #elif ARCH_LIN || ARCH_MAC | ||||
if (plugin->handle) | |||||
dlclose(plugin->handle); | |||||
if (manufacturer->handle) | |||||
dlclose(manufacturer->handle); | |||||
#endif | #endif | ||||
// For some reason this segfaults | // For some reason this segfaults | ||||
// delete plugin; | |||||
// delete manufacturer; | |||||
} | } | ||||
gPlugins.clear(); | |||||
gManufacturers.clear(); | |||||
} | } | ||||
void pluginLogIn(std::string email, std::string password) { | void pluginLogIn(std::string email, std::string password) { | ||||
@@ -42,6 +42,7 @@ void FramebufferWidget::step() { | |||||
internal->box.size = box.size; | internal->box.size = box.size; | ||||
internal->box.size = Vec(ceilf(internal->box.size.x), ceilf(internal->box.size.y)); | internal->box.size = Vec(ceilf(internal->box.size.x), ceilf(internal->box.size.y)); | ||||
Vec fbSize = internal->box.size.mult(gPixelRatio * oversample); | Vec fbSize = internal->box.size.mult(gPixelRatio * oversample); | ||||
// assert(fbSize.isFinite()); | // assert(fbSize.isFinite()); | ||||
// Reject zero area size | // Reject zero area size | ||||
if (fbSize.x <= 0.0 || fbSize.y <= 0.0) | if (fbSize.x <= 0.0 || fbSize.y <= 0.0) | ||||
@@ -71,6 +72,20 @@ void FramebufferWidget::step() { | |||||
} | } | ||||
void FramebufferWidget::draw(NVGcontext *vg) { | 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) { | if (!internal->fb) { | ||||
// Bypass framebuffer cache entirely | // Bypass framebuffer cache entirely | ||||
// Widget::draw(vg); | // Widget::draw(vg); | ||||
@@ -101,6 +101,14 @@ bool TextField::onFocusKey(int key) { | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
case GLFW_KEY_ENTER: | |||||
if (multiline) { | |||||
insertText("\n"); | |||||
} | |||||
else { | |||||
onAction(); | |||||
} | |||||
break; | |||||
} | } | ||||
begin = mini(maxi(begin, 0), text.size()); | begin = mini(maxi(begin, 0), text.size()); | ||||