@@ -60,7 +60,7 @@ debug: $(TARGET) | |||
ifeq ($(ARCH), mac) | |||
lldb ./Rack | |||
else | |||
gdb -ex run ./Rack | |||
LD_LIBRARY_PATH=dep/lib gdb -ex run ./Rack | |||
endif | |||
clean: | |||
@@ -0,0 +1,21 @@ | |||
#pragma once | |||
#include <string> | |||
#include "plugin.hpp" | |||
namespace rack { | |||
/** Searches for a global read-only resource and returns its path, or "" if not found | |||
*/ | |||
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 | |||
*/ | |||
std::string assetPlugin(Plugin *plugin, std::string filename); | |||
} // namespace rack |
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
#include "app.hpp" | |||
#include "asset.hpp" | |||
namespace rack { | |||
@@ -90,163 +91,163 @@ struct Rogan1P : Rogan { | |||
struct Rogan6PSWhite : Rogan6PS { | |||
Rogan6PSWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan6PSWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan6PSWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan5PSGray : Rogan5PS { | |||
Rogan5PSGray() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan5PSGray.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan5PSGray.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSBlue : Rogan3PS { | |||
Rogan3PSBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PSBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PSBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSRed : Rogan3PS { | |||
Rogan3PSRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PSRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PSRed.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSGreen : Rogan3PS { | |||
Rogan3PSGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PSGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PSGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan3PSWhite : Rogan3PS { | |||
Rogan3PSWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PSWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PSWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan3PBlue : Rogan3P { | |||
Rogan3PBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan3PRed : Rogan3P { | |||
Rogan3PRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PRed.svg"))); | |||
} | |||
}; | |||
struct Rogan3PGreen : Rogan3P { | |||
Rogan3PGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan3PWhite : Rogan3P { | |||
Rogan3PWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan3PWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan3PWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan2SGray : Rogan2S { | |||
Rogan2SGray() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2SGray.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2SGray.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSBlue : Rogan2PS { | |||
Rogan2PSBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PSBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PSBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSRed : Rogan2PS { | |||
Rogan2PSRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PSRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PSRed.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSGreen : Rogan2PS { | |||
Rogan2PSGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PSGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PSGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan2PSWhite : Rogan2PS { | |||
Rogan2PSWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PSWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PSWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan2PBlue : Rogan2P { | |||
Rogan2PBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan2PRed : Rogan2P { | |||
Rogan2PRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PRed.svg"))); | |||
} | |||
}; | |||
struct Rogan2PGreen : Rogan2P { | |||
Rogan2PGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan2PWhite : Rogan2P { | |||
Rogan2PWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan2PWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan2PWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSBlue : Rogan1PS { | |||
Rogan1PSBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PSBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PSBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSRed : Rogan1PS { | |||
Rogan1PSRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PSRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PSRed.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSGreen : Rogan1PS { | |||
Rogan1PSGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PSGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PSGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan1PSWhite : Rogan1PS { | |||
Rogan1PSWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PSWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PSWhite.svg"))); | |||
} | |||
}; | |||
struct Rogan1PBlue : Rogan1P { | |||
Rogan1PBlue() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PBlue.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PBlue.svg"))); | |||
} | |||
}; | |||
struct Rogan1PRed : Rogan1P { | |||
Rogan1PRed() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PRed.svg"))); | |||
} | |||
}; | |||
struct Rogan1PGreen : Rogan1P { | |||
Rogan1PGreen() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PGreen.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PGreen.svg"))); | |||
} | |||
}; | |||
struct Rogan1PWhite : Rogan1P { | |||
Rogan1PWhite() { | |||
setSVG(SVG::load("res/ComponentLibrary/Rogan1PWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Rogan1PWhite.svg"))); | |||
} | |||
}; | |||
@@ -256,7 +257,7 @@ struct SynthTechAlco : SVGKnob { | |||
box.size = Vec(45, 45); | |||
minAngle = -0.82*M_PI; | |||
maxAngle = 0.82*M_PI; | |||
setSVG(SVG::load("res/ComponentLibrary/SynthTechAlco.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/SynthTechAlco.svg"))); | |||
} | |||
}; | |||
@@ -270,43 +271,43 @@ struct Davies1900hKnob : SVGKnob { | |||
struct Davies1900hWhiteKnob : Davies1900hKnob { | |||
Davies1900hWhiteKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hWhite.svg"))); | |||
} | |||
}; | |||
struct Davies1900hBlackKnob : Davies1900hKnob { | |||
Davies1900hBlackKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hBlack.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hBlack.svg"))); | |||
} | |||
}; | |||
struct Davies1900hRedKnob : Davies1900hKnob { | |||
Davies1900hRedKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hRed.svg"))); | |||
} | |||
}; | |||
struct Davies1900hLargeWhiteKnob : Davies1900hKnob { | |||
Davies1900hLargeWhiteKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hLargeWhite.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hLargeWhite.svg"))); | |||
} | |||
}; | |||
struct Davies1900hLargeBlackKnob : Davies1900hKnob { | |||
Davies1900hLargeBlackKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hLargeBlack.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hLargeBlack.svg"))); | |||
} | |||
}; | |||
struct Davies1900hLargeRedKnob : Davies1900hKnob { | |||
Davies1900hLargeRedKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hLargeRed.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hLargeRed.svg"))); | |||
} | |||
}; | |||
struct Davies1900hSmallBlackKnob : Davies1900hKnob { | |||
Davies1900hSmallBlackKnob() { | |||
setSVG(SVG::load("res/ComponentLibrary/Davies1900hSmallBlack.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Davies1900hSmallBlack.svg"))); | |||
} | |||
}; | |||
@@ -317,7 +318,7 @@ struct Trimpot : SVGKnob { | |||
box.size = Vec(17, 17); | |||
minAngle = -0.75*M_PI; | |||
maxAngle = 0.75*M_PI; | |||
setSVG(SVG::load("res/ComponentLibrary/Trimpot.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/Trimpot.svg"))); | |||
} | |||
}; | |||
@@ -326,7 +327,7 @@ struct BefacoBigKnob : SVGKnob { | |||
box.size = Vec(75, 75); | |||
minAngle = -0.75*M_PI; | |||
maxAngle = 0.75*M_PI; | |||
setSVG(SVG::load("res/ComponentLibrary/BefacoBigKnob.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/BefacoBigKnob.svg"))); | |||
} | |||
}; | |||
@@ -337,7 +338,7 @@ struct BefacoTinyKnob : SVGKnob { | |||
box.size = Vec(26, 26); | |||
minAngle = -0.75*M_PI; | |||
maxAngle = 0.75*M_PI; | |||
setSVG(SVG::load("res/ComponentLibrary/BefacoTinyKnob.svg")); | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/BefacoTinyKnob.svg"))); | |||
} | |||
}; | |||
@@ -346,11 +347,11 @@ struct BefacoSlidePot : SVGSlider { | |||
Vec margin = Vec(3.5, 3.5); | |||
maxHandlePos = Vec(-1, -2).plus(margin); | |||
minHandlePos = Vec(-1, 87).plus(margin); | |||
background->svg = SVG::load("res/ComponentLibrary/BefacoSlidePot.svg"); | |||
background->svg = SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePot.svg")); | |||
background->wrap(); | |||
background->box.pos = margin; | |||
box.size = background->box.size.plus(margin.mult(2)); | |||
handle->svg = SVG::load("res/ComponentLibrary/BefacoSlidePotHandle.svg"); | |||
handle->svg = SVG::load(assetGlobal("res/ComponentLibrary/BefacoSlidePotHandle.svg")); | |||
handle->wrap(); | |||
} | |||
}; | |||
@@ -361,7 +362,7 @@ struct BefacoSlidePot : SVGSlider { | |||
struct PJ301MPort : SVGPort { | |||
PJ301MPort() { | |||
background->svg = SVG::load("res/ComponentLibrary/PJ301M.svg"); | |||
background->svg = SVG::load(assetGlobal("res/ComponentLibrary/PJ301M.svg")); | |||
background->wrap(); | |||
box.size = background->box.size; | |||
} | |||
@@ -369,7 +370,7 @@ struct PJ301MPort : SVGPort { | |||
struct PJ3410Port : SVGPort { | |||
PJ3410Port() { | |||
background->svg = SVG::load("res/ComponentLibrary/PJ3410.svg"); | |||
background->svg = SVG::load(assetGlobal("res/ComponentLibrary/PJ3410.svg")); | |||
background->wrap(); | |||
box.size = background->box.size; | |||
} | |||
@@ -377,7 +378,7 @@ struct PJ3410Port : SVGPort { | |||
struct CL1362Port : SVGPort { | |||
CL1362Port() { | |||
background->svg = SVG::load("res/ComponentLibrary/CL1362.svg"); | |||
background->svg = SVG::load(assetGlobal("res/ComponentLibrary/CL1362.svg")); | |||
background->wrap(); | |||
box.size = background->box.size; | |||
} | |||
@@ -480,9 +481,9 @@ struct TinyLight : BASE { | |||
struct NKK : SVGSwitch, ToggleSwitch { | |||
NKK() { | |||
addFrame(SVG::load("res/ComponentLibrary/NKK_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/NKK_1.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/NKK_2.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/NKK_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/NKK_1.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/NKK_2.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -490,8 +491,8 @@ struct NKK : SVGSwitch, ToggleSwitch { | |||
struct CKSS : SVGSwitch, ToggleSwitch { | |||
CKSS() { | |||
addFrame(SVG::load("res/ComponentLibrary/CKSS_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/CKSS_1.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/CKSS_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/CKSS_1.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -499,8 +500,8 @@ struct CKSS : SVGSwitch, ToggleSwitch { | |||
struct CKD6 : SVGSwitch, MomentarySwitch { | |||
CKD6() { | |||
addFrame(SVG::load("res/ComponentLibrary/CKD6_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/CKD6_1.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/CKD6_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/CKD6_1.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -508,8 +509,8 @@ struct CKD6 : SVGSwitch, MomentarySwitch { | |||
struct TL1105 : SVGSwitch, MomentarySwitch { | |||
TL1105() { | |||
addFrame(SVG::load("res/ComponentLibrary/TL1105_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/TL1105_1.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/TL1105_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/TL1105_1.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -517,7 +518,7 @@ struct TL1105 : SVGSwitch, MomentarySwitch { | |||
struct LEDButton : SVGSwitch, MomentarySwitch { | |||
LEDButton() { | |||
addFrame(SVG::load("res/ComponentLibrary/LEDButton.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/LEDButton.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -525,9 +526,9 @@ struct LEDButton : SVGSwitch, MomentarySwitch { | |||
struct BefacoSwitch : SVGSwitch, ToggleSwitch { | |||
BefacoSwitch() { | |||
addFrame(SVG::load("res/ComponentLibrary/BefacoSwitch_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/BefacoSwitch_1.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/BefacoSwitch_2.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/BefacoSwitch_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/BefacoSwitch_1.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/BefacoSwitch_2.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -535,8 +536,8 @@ struct BefacoSwitch : SVGSwitch, ToggleSwitch { | |||
struct BefacoPush : SVGSwitch, MomentarySwitch { | |||
BefacoPush() { | |||
addFrame(SVG::load("res/ComponentLibrary/BefacoPush_0.svg")); | |||
addFrame(SVG::load("res/ComponentLibrary/BefacoPush_1.svg")); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/BefacoPush_0.svg"))); | |||
addFrame(SVG::load(assetGlobal("res/ComponentLibrary/BefacoPush_1.svg"))); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -549,7 +550,7 @@ struct BefacoPush : SVGSwitch, MomentarySwitch { | |||
struct ScrewSilver : SVGScrew { | |||
ScrewSilver() { | |||
sw->svg = SVG::load("res/ComponentLibrary/ScrewSilver.svg"); | |||
sw->svg = SVG::load(assetGlobal("res/ComponentLibrary/ScrewSilver.svg")); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -557,7 +558,7 @@ struct ScrewSilver : SVGScrew { | |||
struct ScrewBlack : SVGScrew { | |||
ScrewBlack() { | |||
sw->svg = SVG::load("res/ComponentLibrary/ScrewBlack.svg"); | |||
sw->svg = SVG::load(assetGlobal("res/ComponentLibrary/ScrewBlack.svg")); | |||
sw->wrap(); | |||
box.size = sw->box.size; | |||
} | |||
@@ -13,21 +13,25 @@ struct Model; | |||
struct Plugin { | |||
virtual ~Plugin(); | |||
// A unique identifier for your plugin, e.g. "foo" | |||
/** A unique identifier for your plugin, e.g. "foo" */ | |||
std::string slug; | |||
// Human readable name for your plugin, e.g. "Foo Modular" | |||
/** Human readable name for your plugin, e.g. "Foo Modular" */ | |||
std::string name; | |||
/** The file path of the plugins directory */ | |||
std::string path; | |||
/** A list of the models made available by this plugin */ | |||
std::list<Model*> models; | |||
/** OS-dependent library handle */ | |||
void *handle = NULL; | |||
}; | |||
struct Model { | |||
virtual ~Model() {} | |||
Plugin *plugin; | |||
// A unique identifier for the model in this plugin, e.g. "vco" | |||
/** A unique identifier for the model in this plugin, e.g. "VCO" */ | |||
std::string slug; | |||
// Human readable name for your model, e.g. "VCO" | |||
/** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | |||
std::string name; | |||
virtual ModuleWidget *createModuleWidget() { return NULL; } | |||
}; | |||
@@ -54,8 +58,7 @@ std::string pluginGetLoginStatus(); | |||
// Implemented by plugin | |||
//////////////////// | |||
/** Called once to initialize and return Plugin. | |||
Plugin is destructed when Rack closes | |||
/** Called once to initialize and return the Plugin instance. | |||
*/ | |||
extern "C" | |||
rack::Plugin *init(); | |||
void init(rack::Plugin *plugin); |
@@ -8,6 +8,7 @@ | |||
#include "app.hpp" | |||
#include "components.hpp" | |||
#include "dsp.hpp" | |||
#include "asset.hpp" | |||
namespace rack { | |||
@@ -4,6 +4,8 @@ | |||
#include "engine.hpp" | |||
#include "plugin.hpp" | |||
#include "gui.hpp" | |||
#include "settings.hpp" | |||
#include "asset.hpp" | |||
#include "../ext/osdialog/osdialog.h" | |||
@@ -49,7 +51,7 @@ void RackWidget::clear() { | |||
} | |||
void RackWidget::openDialog() { | |||
std::string dir = lastPath.empty() ? "." : extractDirectory(lastPath); | |||
std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath); | |||
char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL); | |||
if (path) { | |||
loadPatch(path); | |||
@@ -68,7 +70,7 @@ void RackWidget::saveDialog() { | |||
} | |||
void RackWidget::saveAsDialog() { | |||
std::string dir = lastPath.empty() ? "." : extractDirectory(lastPath); | |||
std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath); | |||
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcv", NULL); | |||
if (path) { | |||
savePatch(path); | |||
@@ -333,6 +335,12 @@ void RackWidget::step() { | |||
} | |||
} | |||
// Autosave every 15 seconds | |||
if (gGuiFrame % (60*15) == 0) { | |||
savePatch(assetLocal("autosave.vcv")); | |||
settingsSave(assetLocal("settings.json")); | |||
} | |||
Widget::step(); | |||
} | |||
@@ -0,0 +1,55 @@ | |||
#include "asset.hpp" | |||
#include <stdlib.h> // for realpath | |||
#include <assert.h> | |||
namespace rack { | |||
static std::string fileRealPath(std::string path) { | |||
char *rpath = realpath(path.c_str(), NULL); | |||
if (!rpath) | |||
return ""; | |||
std::string rrpath = path; | |||
free(rpath); | |||
return rrpath; | |||
} | |||
std::string assetGlobal(std::string filename) { | |||
std::string path; | |||
#if ARCH_MAC | |||
// TODO | |||
#endif | |||
#if ARCH_WIN | |||
path = "./" + filename; | |||
#endif | |||
#if ARCH_LIN | |||
path = "./" + filename; | |||
#endif | |||
return fileRealPath(path); | |||
} | |||
std::string assetLocal(std::string filename) { | |||
std::string path; | |||
#if ARCH_MAC | |||
// TODO | |||
#endif | |||
#if ARCH_WIN | |||
// TODO | |||
#endif | |||
#if ARCH_LIN | |||
// TODO | |||
// If Rack is "installed" (however that may be defined), look in ~/.Rack or something instead | |||
path = "./" + filename; | |||
#endif | |||
return fileRealPath(path); | |||
} | |||
std::string assetPlugin(Plugin *plugin, std::string filename) { | |||
assert(plugin); | |||
return fileRealPath(plugin->path + "/" + filename); | |||
} | |||
} // namespace rack |
@@ -1,16 +1,9 @@ | |||
#include "core.hpp" | |||
struct CorePlugin : Plugin { | |||
CorePlugin() { | |||
slug = "Core"; | |||
name = "Core"; | |||
createModel<AudioInterfaceWidget>(this, "AudioInterface", "Audio Interface"); | |||
createModel<MidiInterfaceWidget>(this, "MidiInterface", "MIDI Interface"); | |||
} | |||
}; | |||
Plugin *init() { | |||
return new CorePlugin(); | |||
void init(rack::Plugin *plugin) { | |||
plugin->slug = "Core"; | |||
plugin->name = "Core"; | |||
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface"); | |||
createModel<MidiInterfaceWidget>(plugin, "MidiInterface", "MIDI Interface"); | |||
} |
@@ -3,6 +3,7 @@ | |||
using namespace rack; | |||
//////////////////// | |||
// module widgets | |||
//////////////////// | |||
@@ -2,7 +2,7 @@ | |||
#include "gui.hpp" | |||
#include "app.hpp" | |||
#include "settings.hpp" | |||
#include "asset.hpp" | |||
#include "../ext/osdialog/osdialog.h" | |||
#define NANOVG_GL2_IMPLEMENTATION | |||
@@ -255,9 +255,9 @@ void guiInit() { | |||
assert(gVg); | |||
// Set up Blendish | |||
gGuiFont = Font::load("res/DejaVuSans.ttf"); | |||
gGuiFont = Font::load(assetGlobal("res/DejaVuSans.ttf")); | |||
bndSetFont(gGuiFont->handle); | |||
// bndSetIconImage(loadImage("res/icons.png")); | |||
// bndSetIconImage(loadImage(assetGlobal("res/icons.png"))); | |||
} | |||
void guiDestroy() { | |||
@@ -302,12 +302,6 @@ void guiRun() { | |||
// Render | |||
renderGui(); | |||
// Autosave every 15 seconds | |||
if (gGuiFrame % (60*15) == 0) { | |||
gRackWidget->savePatch("autosave.vcv"); | |||
settingsSave("settings.json"); | |||
} | |||
double currTime = glfwGetTime(); | |||
// printf("%lf fps\n", 1.0/(currTime - lastTime)); | |||
lastTime = currTime; | |||
@@ -3,116 +3,31 @@ | |||
#include "app.hpp" | |||
#include "plugin.hpp" | |||
#include "settings.hpp" | |||
#if ARCH_MAC | |||
#include <CoreFoundation/CoreFoundation.h> | |||
#include <unistd.h> // for chdir and access | |||
#include <libgen.h> // for dirname | |||
// #include <string.h> | |||
#include <mach-o/dyld.h> // for _NSGetExecutablePath | |||
#include <limits.h> // for PATH_MAX? | |||
#include <dirent.h> // for opendir | |||
void alert(std::string header, std::string message, int level) { | |||
CFStringRef headerRef = CFStringCreateWithCString(NULL, header.c_str(), header.size()); | |||
CFStringRef messageRef = CFStringCreateWithCString(NULL, message.c_str(), message.size()); | |||
CFOptionFlags result; | |||
CFUserNotificationDisplayAlert( | |||
0, // no timeout | |||
level, // flags for alert level | |||
NULL, // iconURL | |||
NULL, // soundURL | |||
NULL, // localizationURL | |||
headerRef, | |||
messageRef, | |||
NULL, // default "OK" | |||
NULL, // alternative button | |||
NULL, // other button | |||
&result | |||
); | |||
CFRelease(headerRef); | |||
CFRelease(messageRef); | |||
} | |||
bool isCorrectCwd() { | |||
DIR *dir = opendir("res"); | |||
if (dir) { | |||
closedir(dir); | |||
return true; | |||
} | |||
else { | |||
return false; | |||
} | |||
} | |||
/** macOS workaround for setting the working directory to the location of the .app */ | |||
void fixCwd() { | |||
// Check if the cwd is already set correctly (e.g. launched from the command line or gdb) | |||
if (isCorrectCwd()) | |||
return; | |||
/* | |||
// Get path of binary inside the app bundle | |||
// It should be something like .../Rack.app/Contents/MacOS | |||
char path[PATH_MAX]; | |||
uint32_t pathLen = sizeof(path); | |||
int err = _NSGetExecutablePath(path, &pathLen); | |||
assert(!err); | |||
if (isCorrectCwd()) | |||
return; | |||
// Switch to the directory of the actual binary | |||
chdir(dirname(path)); | |||
if (isCorrectCwd()) | |||
return; | |||
// and then go up three directories to get to the parent directory | |||
chdir("../../../"); | |||
if (isCorrectCwd()) | |||
return; | |||
*/ | |||
// Switch to a default absolute path | |||
chdir("/Applications/Rack"); | |||
if (isCorrectCwd()) | |||
return; | |||
alert("Install Rack", "To install Rack, please move the Rack directory (including the Rack app and plugins directory) to the /Applications folder.", 2); | |||
exit(1); | |||
} | |||
#endif | |||
#include "asset.hpp" | |||
using namespace rack; | |||
int main(int argc, char* argv[]) { | |||
#if ARCH_MAC | |||
fixCwd(); | |||
#endif | |||
pluginInit(); | |||
engineInit(); | |||
guiInit(); | |||
sceneInit(); | |||
settingsLoad("settings.json"); | |||
settingsLoad(assetLocal("settings.json")); | |||
if (argc >= 2) | |||
gRackWidget->loadPatch(argv[1]); | |||
else | |||
gRackWidget->loadPatch("autosave.vcv"); | |||
gRackWidget->loadPatch(assetLocal("autosave.vcv")); | |||
engineStart(); | |||
guiRun(); | |||
engineStop(); | |||
gRackWidget->savePatch("autosave.vcv"); | |||
settingsSave("settings.json"); | |||
gRackWidget->savePatch(assetLocal("autosave.vcv")); | |||
settingsSave(assetLocal("settings.json")); | |||
sceneDestroy(); | |||
guiDestroy(); | |||
engineDestroy(); | |||
pluginDestroy(); | |||
return 0; | |||
} | |||
@@ -22,6 +22,7 @@ | |||
#include "plugin.hpp" | |||
#include "app.hpp" | |||
#include "asset.hpp" | |||
#include "util/request.hpp" | |||
@@ -43,55 +44,70 @@ Plugin::~Plugin() { | |||
} | |||
} | |||
static int loadPlugin(std::string slug) { | |||
#if ARCH_LIN | |||
std::string path = "./plugins/" + slug + "/plugin.so"; | |||
#elif ARCH_WIN | |||
std::string path = "./plugins/" + slug + "/plugin.dll"; | |||
#elif ARCH_MAC | |||
std::string path = "./plugins/" + slug + "/plugin.dylib"; | |||
#endif | |||
static int loadPlugin(std::string path) { | |||
std::string libraryFilename; | |||
#if ARCH_LIN | |||
libraryFilename = path + "/" + "plugin.so"; | |||
#elif ARCH_WIN | |||
libraryFilename = path + "/" + "plugin.dll"; | |||
#elif ARCH_MAC | |||
libraryFilename = path + "/" + "plugin.dylib"; | |||
#endif | |||
// Load dynamic/shared library | |||
#if ARCH_WIN | |||
HINSTANCE handle = LoadLibrary(path.c_str()); | |||
if (!handle) { | |||
int error = GetLastError(); | |||
fprintf(stderr, "Failed to load library %s: %d\n", path.c_str(), error); | |||
return -1; | |||
} | |||
#elif ARCH_LIN || ARCH_MAC | |||
void *handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); | |||
if (!handle) { | |||
fprintf(stderr, "Failed to load library %s: %s\n", path.c_str(), dlerror()); | |||
return -1; | |||
} | |||
#endif | |||
#if ARCH_WIN | |||
HINSTANCE handle = LoadLibrary(libraryFilename.c_str()); | |||
if (!handle) { | |||
int error = GetLastError(); | |||
fprintf(stderr, "Failed to load library %s: %d\n", libraryFilename.c_str(), error); | |||
return -1; | |||
} | |||
#elif ARCH_LIN || ARCH_MAC | |||
void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW); | |||
if (!handle) { | |||
fprintf(stderr, "Failed to load library %s: %s\n", libraryFilename.c_str(), dlerror()); | |||
return -1; | |||
} | |||
#endif | |||
// Call plugin init() function | |||
typedef Plugin *(*InitCallback)(); | |||
typedef void (*InitCallback)(Plugin *); | |||
InitCallback initCallback; | |||
#if ARCH_WIN | |||
initCallback = (InitCallback) GetProcAddress(handle, "init"); | |||
#elif ARCH_LIN || ARCH_MAC | |||
initCallback = (InitCallback) dlsym(handle, "init"); | |||
#endif | |||
#if ARCH_WIN | |||
initCallback = (InitCallback) GetProcAddress(handle, "init"); | |||
#elif ARCH_LIN || ARCH_MAC | |||
initCallback = (InitCallback) dlsym(handle, "init"); | |||
#endif | |||
if (!initCallback) { | |||
fprintf(stderr, "Failed to read init() symbol in %s\n", path.c_str()); | |||
fprintf(stderr, "Failed to read init() symbol in %s\n", libraryFilename.c_str()); | |||
return -2; | |||
} | |||
// Add plugin to map | |||
Plugin *plugin = initCallback(); | |||
if (!plugin) { | |||
fprintf(stderr, "Library %s did not return a plugin\n", path.c_str()); | |||
return -3; | |||
} | |||
// Construct and initialize Plugin instance | |||
Plugin *plugin = new Plugin(); | |||
plugin->path = path; | |||
plugin->handle = handle; | |||
initCallback(plugin); | |||
// Add plugin to list | |||
gPlugins.push_back(plugin); | |||
fprintf(stderr, "Loaded plugin %s\n", path.c_str()); | |||
fprintf(stderr, "Loaded plugin %s\n", libraryFilename.c_str()); | |||
return 0; | |||
} | |||
static void loadPlugins(std::string path) { | |||
DIR *dir = opendir(path.c_str()); | |||
if (dir) { | |||
struct dirent *d; | |||
while ((d = readdir(dir))) { | |||
if (d->d_name[0] == '.') | |||
continue; | |||
loadPlugin(path + "/" + d->d_name); | |||
} | |||
closedir(dir); | |||
} | |||
} | |||
//////////////////// | |||
// plugin helpers | |||
//////////////////// | |||
@@ -178,14 +194,13 @@ static void refreshPurchase(json_t *pluginJ) { | |||
downloadProgress = 0.0; | |||
// Download zip | |||
std::string filename = "plugins/"; | |||
mkdir(filename.c_str(), 0755); | |||
filename += slug; | |||
filename += ".zip"; | |||
std::string pluginsDir = assetLocal("plugins"); | |||
mkdir(pluginsDir.c_str(), 0755); | |||
std::string filename = pluginsDir + "/" + slug + ".zip"; | |||
bool success = requestDownload(download, filename, &downloadProgress); | |||
if (success) { | |||
// Unzip file | |||
int err = extractZip(filename.c_str(), "plugins"); | |||
int err = extractZip(filename.c_str(), pluginsDir.c_str()); | |||
if (!err) { | |||
// Load plugin | |||
loadPlugin(slug); | |||
@@ -204,26 +219,33 @@ static void refreshPurchase(json_t *pluginJ) { | |||
void pluginInit() { | |||
// Load core | |||
// This function is defined in core.cpp | |||
Plugin *corePlugin = init(); | |||
Plugin *corePlugin = new Plugin(); | |||
init(corePlugin); | |||
gPlugins.push_back(corePlugin); | |||
// Search for plugin libraries | |||
DIR *dir = opendir("plugins"); | |||
if (dir) { | |||
struct dirent *d; | |||
while ((d = readdir(dir))) { | |||
if (d->d_name[0] == '.') | |||
continue; | |||
loadPlugin(d->d_name); | |||
} | |||
closedir(dir); | |||
} | |||
// Load plugins from global directory | |||
std::string globalPlugins = assetGlobal("plugins"); | |||
loadPlugins(globalPlugins); | |||
// Load plugins from local directory | |||
std::string localPlugins = assetLocal("plugins"); | |||
if (globalPlugins != localPlugins) | |||
loadPlugins(localPlugins); | |||
} | |||
void pluginDestroy() { | |||
for (Plugin *plugin : gPlugins) { | |||
// TODO free shared library handle with `dlclose` or `FreeLibrary` | |||
delete plugin; | |||
// Free library handle | |||
#if ARCH_WIN | |||
if (plugin->handle) | |||
FreeLibrary(plugin->handle); | |||
#elif ARCH_LIN || ARCH_MAC | |||
if (plugin->handle) | |||
dlclose(plugin->handle); | |||
#endif | |||
// For some reason this segfaults | |||
// delete plugin; | |||
} | |||
gPlugins.clear(); | |||
} | |||