@@ -13,6 +13,8 @@ struct PatchManager { | |||||
int legacy; | int legacy; | ||||
std::string warningLog; | std::string warningLog; | ||||
PatchManager(); | |||||
~PatchManager(); | |||||
void init(std::string path); | void init(std::string path); | ||||
void reset(); | void reset(); | ||||
void resetDialog(); | void resetDialog(); | ||||
@@ -26,7 +26,6 @@ bool isSlugValid(std::string slug); | |||||
extern std::list<Plugin*> plugins; | extern std::list<Plugin*> plugins; | ||||
extern std::string token; | |||||
extern bool isDownloading; | extern bool isDownloading; | ||||
extern float downloadProgress; | extern float downloadProgress; | ||||
extern std::string downloadName; | extern std::string downloadName; | ||||
@@ -1,26 +1,39 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "math.hpp" | |||||
#include <jansson.h> | |||||
namespace rack { | namespace rack { | ||||
namespace settings { | |||||
void save(std::string filename); | |||||
void load(std::string filename); | |||||
struct Settings { | |||||
std::string token; | |||||
math::Vec windowSize; | |||||
math::Vec windowPos; | |||||
float zoom = 1.0; | |||||
float cableOpacity = 0.5; | |||||
float cableTension = 0.5; | |||||
bool allowCursorLock = true; | |||||
float sampleRate = 44100.0; | |||||
int threadCount = 1; | |||||
bool paramTooltip = false; | |||||
bool cpuMeter = false; | |||||
bool lockModules = false; | |||||
bool checkVersion = true; | |||||
float frameRateLimit = 0.0; | |||||
bool frameRateSync = true; | |||||
bool skipLoadOnLaunch = false; | |||||
std::string patchPath; | |||||
json_t *toJson(); | |||||
void fromJson(json_t *rootJ); | |||||
void save(std::string filename); | |||||
void load(std::string filename); | |||||
}; | |||||
extern float zoom; | |||||
extern float cableOpacity; | |||||
extern float cableTension; | |||||
extern bool paramTooltip; | |||||
extern bool powerMeter; | |||||
extern bool lockModules; | |||||
extern bool checkVersion; | |||||
extern bool skipLoadOnLaunch; | |||||
extern float frameRateLimit; | |||||
extern bool frameRateSync; | |||||
extern Settings settings; | |||||
} // namespace settings | |||||
} // namespace rack | } // namespace rack |
@@ -71,7 +71,6 @@ struct Window { | |||||
This is not equal to gPixelRatio in general. | This is not equal to gPixelRatio in general. | ||||
*/ | */ | ||||
float windowRatio = 1.f; | float windowRatio = 1.f; | ||||
bool allowCursorLock = true; | |||||
int frame = 0; | int frame = 0; | ||||
/** The last known absolute mouse position in the window */ | /** The last known absolute mouse position in the window */ | ||||
math::Vec mousePos; | math::Vec mousePos; | ||||
@@ -90,11 +89,6 @@ struct Window { | |||||
Don't call this from a Key event. Simply use `e.mods` instead. | Don't call this from a Key event. Simply use `e.mods` instead. | ||||
*/ | */ | ||||
int getMods(); | int getMods(); | ||||
math::Vec getWindowSize(); | |||||
void setWindowSize(math::Vec size); | |||||
math::Vec getWindowPos(); | |||||
void setWindowPos(math::Vec pos); | |||||
bool isMaximized(); | |||||
void setFullScreen(bool fullScreen); | void setFullScreen(bool fullScreen); | ||||
bool isFullScreen(); | bool isFullScreen(); | ||||
}; | }; | ||||
@@ -207,8 +207,8 @@ void CableWidget::fromJson(json_t *rootJ, const std::map<int, ModuleWidget*> &mo | |||||
} | } | ||||
void CableWidget::draw(const widget::DrawContext &ctx) { | void CableWidget::draw(const widget::DrawContext &ctx) { | ||||
float opacity = settings::cableOpacity; | |||||
float tension = settings::cableTension; | |||||
float opacity = settings.cableOpacity; | |||||
float tension = settings.cableTension; | |||||
float thickness = 5; | float thickness = 5; | ||||
if (isComplete()) { | if (isComplete()) { | ||||
@@ -145,7 +145,7 @@ void ModuleWidget::draw(const widget::DrawContext &ctx) { | |||||
widget::Widget::draw(ctx); | widget::Widget::draw(ctx); | ||||
// Power meter | // Power meter | ||||
if (module && settings::powerMeter) { | |||||
if (module && settings.cpuMeter) { | |||||
nvgBeginPath(ctx.vg); | nvgBeginPath(ctx.vg); | ||||
nvgRect(ctx.vg, | nvgRect(ctx.vg, | ||||
0, box.size.y - 20, | 0, box.size.y - 20, | ||||
@@ -285,7 +285,7 @@ void ModuleWidget::onDragEnd(const event::DragEnd &e) { | |||||
} | } | ||||
void ModuleWidget::onDragMove(const event::DragMove &e) { | void ModuleWidget::onDragMove(const event::DragMove &e) { | ||||
if (!settings::lockModules) { | |||||
if (!settings.lockModules) { | |||||
math::Rect newBox = box; | math::Rect newBox = box; | ||||
newBox.pos = APP->scene->rackWidget->mousePos.minus(dragPos); | newBox.pos = APP->scene->rackWidget->mousePos.minus(dragPos); | ||||
APP->scene->rackWidget->requestModuleBoxNearest(this, newBox); | APP->scene->rackWidget->requestModuleBoxNearest(this, newBox); | ||||
@@ -158,7 +158,7 @@ void ParamWidget::onDoubleClick(const event::DoubleClick &e) { | |||||
} | } | ||||
void ParamWidget::onEnter(const event::Enter &e) { | void ParamWidget::onEnter(const event::Enter &e) { | ||||
if (settings::paramTooltip && !tooltip && paramQuantity) { | |||||
if (settings.paramTooltip && !tooltip && paramQuantity) { | |||||
ParamTooltip *paramTooltip = new ParamTooltip; | ParamTooltip *paramTooltip = new ParamTooltip; | ||||
paramTooltip->paramWidget = this; | paramTooltip->paramWidget = this; | ||||
APP->scene->addChild(paramTooltip); | APP->scene->addChild(paramTooltip); | ||||
@@ -59,12 +59,12 @@ void Scene::step() { | |||||
int frame = APP->window->frame; | int frame = APP->window->frame; | ||||
if (frame > 0 && frame % (60 * 15) == 0) { | if (frame > 0 && frame % (60 * 15) == 0) { | ||||
APP->patch->save(asset::user("autosave.vcv")); | APP->patch->save(asset::user("autosave.vcv")); | ||||
settings::save(asset::user("settings.json")); | |||||
settings.save(asset::user("settings.json")); | |||||
} | } | ||||
// Set zoom every few frames | // Set zoom every few frames | ||||
if (APP->window->frame % 10 == 0) | if (APP->window->frame % 10 == 0) | ||||
zoomWidget->setZoom(std::round(settings::zoom * 100) / 100); | |||||
zoomWidget->setZoom(std::round(settings.zoom * 100) / 100); | |||||
// Request latest version from server | // Request latest version from server | ||||
if (!devMode && checkVersion && !checkedVersion) { | if (!devMode && checkVersion && !checkedVersion) { | ||||
@@ -180,10 +180,10 @@ struct EditButton : MenuButton { | |||||
struct ZoomQuantity : ui::Quantity { | struct ZoomQuantity : ui::Quantity { | ||||
void setValue(float value) override { | void setValue(float value) override { | ||||
settings::zoom = math::clamp(value, getMinValue(), getMaxValue()); | |||||
settings.zoom = math::clamp(value, getMinValue(), getMaxValue()); | |||||
} | } | ||||
float getValue() override { | float getValue() override { | ||||
return settings::zoom; | |||||
return settings.zoom; | |||||
} | } | ||||
float getMinValue() override {return 0.25;} | float getMinValue() override {return 0.25;} | ||||
float getMaxValue() override {return 2.0;} | float getMaxValue() override {return 2.0;} | ||||
@@ -197,10 +197,10 @@ struct ZoomQuantity : ui::Quantity { | |||||
struct CableOpacityQuantity : ui::Quantity { | struct CableOpacityQuantity : ui::Quantity { | ||||
void setValue(float value) override { | void setValue(float value) override { | ||||
settings::cableOpacity = math::clamp(value, getMinValue(), getMaxValue()); | |||||
settings.cableOpacity = math::clamp(value, getMinValue(), getMaxValue()); | |||||
} | } | ||||
float getValue() override { | float getValue() override { | ||||
return settings::cableOpacity; | |||||
return settings.cableOpacity; | |||||
} | } | ||||
float getDefaultValue() override {return 0.5;} | float getDefaultValue() override {return 0.5;} | ||||
float getDisplayValue() override {return getValue() * 100;} | float getDisplayValue() override {return getValue() * 100;} | ||||
@@ -213,10 +213,10 @@ struct CableOpacityQuantity : ui::Quantity { | |||||
struct CableTensionQuantity : ui::Quantity { | struct CableTensionQuantity : ui::Quantity { | ||||
void setValue(float value) override { | void setValue(float value) override { | ||||
settings::cableTension = math::clamp(value, getMinValue(), getMaxValue()); | |||||
settings.cableTension = math::clamp(value, getMinValue(), getMaxValue()); | |||||
} | } | ||||
float getValue() override { | float getValue() override { | ||||
return settings::cableTension; | |||||
return settings.cableTension; | |||||
} | } | ||||
float getDefaultValue() override {return 0.5;} | float getDefaultValue() override {return 0.5;} | ||||
std::string getLabel() override {return "Cable tension";} | std::string getLabel() override {return "Cable tension";} | ||||
@@ -224,13 +224,13 @@ struct CableTensionQuantity : ui::Quantity { | |||||
}; | }; | ||||
struct PowerMeterItem : ui::MenuItem { | |||||
PowerMeterItem() { | |||||
struct CpuMeterItem : ui::MenuItem { | |||||
CpuMeterItem() { | |||||
text = "CPU meter"; | text = "CPU meter"; | ||||
rightText = CHECKMARK(settings::powerMeter); | |||||
rightText = CHECKMARK(settings.cpuMeter); | |||||
} | } | ||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
settings::powerMeter ^= true; | |||||
settings.cpuMeter ^= true; | |||||
} | } | ||||
}; | }; | ||||
@@ -238,10 +238,10 @@ struct PowerMeterItem : ui::MenuItem { | |||||
struct ParamTooltipItem : ui::MenuItem { | struct ParamTooltipItem : ui::MenuItem { | ||||
ParamTooltipItem() { | ParamTooltipItem() { | ||||
text = "Parameter tooltips"; | text = "Parameter tooltips"; | ||||
rightText = CHECKMARK(settings::paramTooltip); | |||||
rightText = CHECKMARK(settings.paramTooltip); | |||||
} | } | ||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
settings::paramTooltip ^= true; | |||||
settings.paramTooltip ^= true; | |||||
} | } | ||||
}; | }; | ||||
@@ -249,10 +249,10 @@ struct ParamTooltipItem : ui::MenuItem { | |||||
struct LockModulesItem : ui::MenuItem { | struct LockModulesItem : ui::MenuItem { | ||||
LockModulesItem() { | LockModulesItem() { | ||||
text = "Lock modules"; | text = "Lock modules"; | ||||
rightText = CHECKMARK(settings::lockModules); | |||||
rightText = CHECKMARK(settings.lockModules); | |||||
} | } | ||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
settings::lockModules ^= true; | |||||
settings.lockModules ^= true; | |||||
} | } | ||||
}; | }; | ||||
@@ -369,7 +369,7 @@ struct SettingsButton : MenuButton { | |||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
menu->addChild(new ParamTooltipItem); | menu->addChild(new ParamTooltipItem); | ||||
menu->addChild(new PowerMeterItem); | |||||
menu->addChild(new CpuMeterItem); | |||||
menu->addChild(new LockModulesItem); | menu->addChild(new LockModulesItem); | ||||
menu->addChild(new SampleRateItem); | menu->addChild(new SampleRateItem); | ||||
menu->addChild(new ThreadCount); | menu->addChild(new ThreadCount); | ||||
@@ -627,10 +627,10 @@ struct WebsiteItem : ui::MenuItem { | |||||
struct CheckVersionItem : ui::MenuItem { | struct CheckVersionItem : ui::MenuItem { | ||||
CheckVersionItem() { | CheckVersionItem() { | ||||
text = "Check version on launch"; | text = "Check version on launch"; | ||||
rightText = CHECKMARK(settings::checkVersion); | |||||
rightText = CHECKMARK(settings.checkVersion); | |||||
} | } | ||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
settings::checkVersion ^= true; | |||||
settings.checkVersion ^= true; | |||||
} | } | ||||
}; | }; | ||||
@@ -155,16 +155,20 @@ struct Engine::Internal { | |||||
Engine::Engine() { | Engine::Engine() { | ||||
internal = new Internal; | internal = new Internal; | ||||
float sampleRate = 44100.f; | |||||
float sampleRate = settings.sampleRate; | |||||
internal->sampleRate = sampleRate; | internal->sampleRate = sampleRate; | ||||
internal->sampleTime = 1 / sampleRate; | internal->sampleTime = 1 / sampleRate; | ||||
internal->sampleRateRequested = sampleRate; | internal->sampleRateRequested = sampleRate; | ||||
internal->threadCount = settings.threadCount; | |||||
internal->engineBarrier.total = 1; | internal->engineBarrier.total = 1; | ||||
internal->workerBarrier.total = 1; | internal->workerBarrier.total = 1; | ||||
} | } | ||||
Engine::~Engine() { | Engine::~Engine() { | ||||
settings.sampleRate = internal->sampleRate; | |||||
settings.threadCount = internal->threadCount; | |||||
// Make sure there are no cables or modules in the rack on destruction. This suggests that a module failed to remove itself before the RackWidget was destroyed. | // Make sure there are no cables or modules in the rack on destruction. This suggests that a module failed to remove itself before the RackWidget was destroyed. | ||||
assert(internal->cables.empty()); | assert(internal->cables.empty()); | ||||
assert(internal->modules.empty()); | assert(internal->modules.empty()); | ||||
@@ -173,6 +177,7 @@ Engine::~Engine() { | |||||
} | } | ||||
static void Engine_setWorkerCount(Engine *engine, int workerCount) { | static void Engine_setWorkerCount(Engine *engine, int workerCount) { | ||||
assert(0 <= workerCount && workerCount <= 32); | |||||
Engine::Internal *internal = engine->internal; | Engine::Internal *internal = engine->internal; | ||||
// Stop all workers | // Stop all workers | ||||
@@ -213,7 +218,7 @@ static void Engine_stepModules(Engine *engine, int id) { | |||||
Module *module = internal->modules[i]; | Module *module = internal->modules[i]; | ||||
if (!module->bypass) { | if (!module->bypass) { | ||||
// Step module | // Step module | ||||
if (settings::powerMeter) { | |||||
if (settings.cpuMeter) { | |||||
auto startTime = std::chrono::high_resolution_clock::now(); | auto startTime = std::chrono::high_resolution_clock::now(); | ||||
module->step(); | module->step(); | ||||
@@ -83,9 +83,9 @@ int main(int argc, char *argv[]) { | |||||
INFO("Initialized environment"); | INFO("Initialized environment"); | ||||
// Initialize app | // Initialize app | ||||
settings.load(asset::user("settings.json")); | |||||
app::init(); | app::init(); | ||||
APP->scene->devMode = devMode; | APP->scene->devMode = devMode; | ||||
settings::load(asset::user("settings.json")); | |||||
APP->patch->init(patchPath); | APP->patch->init(patchPath); | ||||
INFO("Initialized app"); | INFO("Initialized app"); | ||||
@@ -97,8 +97,8 @@ int main(int argc, char *argv[]) { | |||||
// Destroy app | // Destroy app | ||||
APP->patch->save(asset::user("autosave.vcv")); | APP->patch->save(asset::user("autosave.vcv")); | ||||
settings::save(asset::user("settings.json")); | |||||
app::destroy(); | app::destroy(); | ||||
settings.save(asset::user("settings.json")); | |||||
INFO("Cleaned up app"); | INFO("Cleaned up app"); | ||||
// Destroy environment | // Destroy environment | ||||
@@ -16,6 +16,14 @@ namespace rack { | |||||
static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv"; | static const char PATCH_FILTERS[] = "VCV Rack patch (.vcv):vcv"; | ||||
PatchManager::PatchManager() { | |||||
path = settings.patchPath; | |||||
} | |||||
PatchManager::~PatchManager() { | |||||
settings.patchPath = path; | |||||
} | |||||
void PatchManager::init(std::string path) { | void PatchManager::init(std::string path) { | ||||
if (!path.empty()) { | if (!path.empty()) { | ||||
// Load patch | // Load patch | ||||
@@ -25,10 +33,10 @@ void PatchManager::init(std::string path) { | |||||
} | } | ||||
// To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded. | // To prevent launch crashes, if Rack crashes between now and 15 seconds from now, the "skipAutosaveOnLaunch" property will remain in settings.json, so that in the next launch, the broken autosave will not be loaded. | ||||
bool oldSkipLoadOnLaunch = settings::skipLoadOnLaunch; | |||||
settings::skipLoadOnLaunch = true; | |||||
settings::save(asset::user("settings.json")); | |||||
settings::skipLoadOnLaunch = false; | |||||
bool oldSkipLoadOnLaunch = settings.skipLoadOnLaunch; | |||||
settings.skipLoadOnLaunch = true; | |||||
settings.save(asset::user("settings.json")); | |||||
settings.skipLoadOnLaunch = false; | |||||
if (oldSkipLoadOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) { | if (oldSkipLoadOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Clear your patch and start over?")) { | ||||
this->path = ""; | this->path = ""; | ||||
return; | return; | ||||
@@ -6,6 +6,7 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "app/common.hpp" | #include "app/common.hpp" | ||||
#include "plugin/callbacks.hpp" | #include "plugin/callbacks.hpp" | ||||
#include "settings.hpp" | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
@@ -184,7 +185,7 @@ static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) { | |||||
if (dryRun) { | if (dryRun) { | ||||
downloadUrl += "/available"; | downloadUrl += "/available"; | ||||
} | } | ||||
downloadUrl += "?token=" + network::encodeUrl(token); | |||||
downloadUrl += "?token=" + network::encodeUrl(settings.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); | ||||
@@ -393,7 +394,7 @@ void logIn(std::string email, std::string password) { | |||||
json_t *tokenJ = json_object_get(resJ, "token"); | json_t *tokenJ = json_object_get(resJ, "token"); | ||||
if (tokenJ) { | if (tokenJ) { | ||||
const char *tokenStr = json_string_value(tokenJ); | const char *tokenStr = json_string_value(tokenJ); | ||||
token = tokenStr; | |||||
settings.token = tokenStr; | |||||
loginStatus = ""; | loginStatus = ""; | ||||
} | } | ||||
} | } | ||||
@@ -402,11 +403,11 @@ void logIn(std::string email, std::string password) { | |||||
} | } | ||||
void logOut() { | void logOut() { | ||||
token = ""; | |||||
settings.token = ""; | |||||
} | } | ||||
bool sync(bool dryRun) { | bool sync(bool dryRun) { | ||||
if (token.empty()) | |||||
if (settings.token.empty()) | |||||
return false; | return false; | ||||
bool available = false; | bool available = false; | ||||
@@ -422,7 +423,7 @@ bool sync(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(token.c_str())); | |||||
json_object_set(pluginsReqJ, "token", json_string(settings.token.c_str())); | |||||
std::string pluginsUrl = app::API_URL; | std::string pluginsUrl = app::API_URL; | ||||
pluginsUrl += "/plugins"; | pluginsUrl += "/plugins"; | ||||
json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ); | json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ); | ||||
@@ -493,7 +494,7 @@ void cancelDownload() { | |||||
} | } | ||||
bool isLoggedIn() { | bool isLoggedIn() { | ||||
return token != ""; | |||||
return settings.token != ""; | |||||
} | } | ||||
Plugin *getPlugin(std::string pluginSlug) { | Plugin *getPlugin(std::string pluginSlug) { | ||||
@@ -534,7 +535,6 @@ bool isSlugValid(std::string slug) { | |||||
std::list<Plugin*> plugins; | std::list<Plugin*> plugins; | ||||
std::string token; | |||||
bool isDownloading = false; | bool isDownloading = false; | ||||
float downloadProgress = 0.f; | float downloadProgress = 0.f; | ||||
std::string downloadName; | std::string downloadName; | ||||
@@ -10,181 +10,137 @@ | |||||
namespace rack { | namespace rack { | ||||
namespace settings { | |||||
static json_t *settingsToJson() { | |||||
// root | |||||
json_t *Settings::toJson() { | |||||
json_t *rootJ = json_object(); | json_t *rootJ = json_object(); | ||||
// token | |||||
json_t *tokenJ = json_string(plugin::token.c_str()); | |||||
json_object_set_new(rootJ, "token", tokenJ); | |||||
json_object_set_new(rootJ, "token", json_string(token.c_str())); | |||||
if (!APP->window->isMaximized()) { | |||||
// windowSize | |||||
math::Vec windowSize = APP->window->getWindowSize(); | |||||
json_t *windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y); | |||||
json_object_set_new(rootJ, "windowSize", windowSizeJ); | |||||
json_t *windowSizeJ = json_pack("[f, f]", windowSize.x, windowSize.y); | |||||
json_object_set_new(rootJ, "windowSize", windowSizeJ); | |||||
// windowPos | |||||
math::Vec windowPos = APP->window->getWindowPos(); | |||||
json_t *windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y); | |||||
json_object_set_new(rootJ, "windowPos", windowPosJ); | |||||
} | |||||
// cableOpacity | |||||
json_t *cableOpacityJ = json_real(cableOpacity); | |||||
json_object_set_new(rootJ, "cableOpacity", cableOpacityJ); | |||||
json_t *windowPosJ = json_pack("[f, f]", windowPos.x, windowPos.y); | |||||
json_object_set_new(rootJ, "windowPos", windowPosJ); | |||||
// cableTension | |||||
json_t *cableTensionJ = json_real(cableTension); | |||||
json_object_set_new(rootJ, "cableTension", cableTensionJ); | |||||
json_object_set_new(rootJ, "zoom", json_real(zoom)); | |||||
// zoom | |||||
json_t *zoomJ = json_real(zoom); | |||||
json_object_set_new(rootJ, "zoom", zoomJ); | |||||
json_object_set_new(rootJ, "cableOpacity", json_real(cableOpacity)); | |||||
// allowCursorLock | |||||
json_t *allowCursorLockJ = json_boolean(APP->window->allowCursorLock); | |||||
json_object_set_new(rootJ, "allowCursorLock", allowCursorLockJ); | |||||
json_object_set_new(rootJ, "cableTension", json_real(cableTension)); | |||||
// sampleRate | |||||
json_t *sampleRateJ = json_real(APP->engine->getSampleRate()); | |||||
json_object_set_new(rootJ, "sampleRate", sampleRateJ); | |||||
json_object_set_new(rootJ, "allowCursorLock", json_boolean(allowCursorLock)); | |||||
// patchPath | |||||
json_t *patchPathJ = json_string(APP->patch->path.c_str()); | |||||
json_object_set_new(rootJ, "patchPath", patchPathJ); | |||||
json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); | |||||
// skipLoadOnLaunch | |||||
if (skipLoadOnLaunch) { | |||||
json_object_set_new(rootJ, "skipLoadOnLaunch", json_true()); | |||||
} | |||||
json_object_set_new(rootJ, "threadCount", json_integer(threadCount)); | |||||
// moduleBrowser | |||||
json_object_set_new(rootJ, "moduleBrowser", app::moduleBrowserToJson()); | |||||
json_object_set_new(rootJ, "paramTooltip", json_boolean(paramTooltip)); | |||||
// powerMeter | |||||
json_object_set_new(rootJ, "powerMeter", json_boolean(powerMeter)); | |||||
json_object_set_new(rootJ, "cpuMeter", json_boolean(cpuMeter)); | |||||
// threadCount | |||||
json_object_set_new(rootJ, "threadCount", json_integer(APP->engine->getThreadCount())); | |||||
json_object_set_new(rootJ, "lockModules", json_boolean(lockModules)); | |||||
// checkVersion | |||||
json_object_set_new(rootJ, "checkVersion", json_boolean(checkVersion)); | json_object_set_new(rootJ, "checkVersion", json_boolean(checkVersion)); | ||||
// paramTooltip | |||||
json_object_set_new(rootJ, "paramTooltip", json_boolean(paramTooltip)); | |||||
// frameRateLimit | |||||
json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit)); | json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit)); | ||||
// frameRateSync | |||||
json_object_set_new(rootJ, "frameRateSync", json_boolean(frameRateSync)); | json_object_set_new(rootJ, "frameRateSync", json_boolean(frameRateSync)); | ||||
if (skipLoadOnLaunch) { | |||||
json_object_set_new(rootJ, "skipLoadOnLaunch", json_true()); | |||||
} | |||||
json_object_set_new(rootJ, "patchPath", json_string(patchPath.c_str())); | |||||
json_object_set_new(rootJ, "moduleBrowser", app::moduleBrowserToJson()); | |||||
return rootJ; | return rootJ; | ||||
} | } | ||||
static void settingsFromJson(json_t *rootJ) { | |||||
// token | |||||
void Settings::fromJson(json_t *rootJ) { | |||||
json_t *tokenJ = json_object_get(rootJ, "token"); | json_t *tokenJ = json_object_get(rootJ, "token"); | ||||
if (tokenJ) | if (tokenJ) | ||||
plugin::token = json_string_value(tokenJ); | |||||
token = json_string_value(tokenJ); | |||||
// windowSize | |||||
json_t *windowSizeJ = json_object_get(rootJ, "windowSize"); | json_t *windowSizeJ = json_object_get(rootJ, "windowSize"); | ||||
if (windowSizeJ) { | if (windowSizeJ) { | ||||
double width, height; | |||||
json_unpack(windowSizeJ, "[F, F]", &width, &height); | |||||
APP->window->setWindowSize(math::Vec(width, height)); | |||||
double x, y; | |||||
json_unpack(windowSizeJ, "[F, F]", &x, &y); | |||||
windowSize = math::Vec(x, y); | |||||
} | } | ||||
// windowPos | |||||
json_t *windowPosJ = json_object_get(rootJ, "windowPos"); | json_t *windowPosJ = json_object_get(rootJ, "windowPos"); | ||||
if (windowPosJ) { | if (windowPosJ) { | ||||
double x, y; | double x, y; | ||||
json_unpack(windowPosJ, "[F, F]", &x, &y); | json_unpack(windowPosJ, "[F, F]", &x, &y); | ||||
APP->window->setWindowPos(math::Vec(x, y)); | |||||
windowPos = math::Vec(x, y); | |||||
} | } | ||||
// cableOpacity | |||||
json_t *zoomJ = json_object_get(rootJ, "zoom"); | |||||
if (zoomJ) | |||||
zoom = json_number_value(zoomJ); | |||||
json_t *cableOpacityJ = json_object_get(rootJ, "cableOpacity"); | json_t *cableOpacityJ = json_object_get(rootJ, "cableOpacity"); | ||||
if (cableOpacityJ) | if (cableOpacityJ) | ||||
cableOpacity = json_number_value(cableOpacityJ); | cableOpacity = json_number_value(cableOpacityJ); | ||||
// tension | |||||
json_t *tensionJ = json_object_get(rootJ, "cableTension"); | json_t *tensionJ = json_object_get(rootJ, "cableTension"); | ||||
if (tensionJ) | if (tensionJ) | ||||
cableTension = json_number_value(tensionJ); | cableTension = json_number_value(tensionJ); | ||||
// zoom | |||||
json_t *zoomJ = json_object_get(rootJ, "zoom"); | |||||
if (zoomJ) | |||||
zoom = json_number_value(zoomJ); | |||||
// allowCursorLock | |||||
json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); | json_t *allowCursorLockJ = json_object_get(rootJ, "allowCursorLock"); | ||||
if (allowCursorLockJ) | if (allowCursorLockJ) | ||||
APP->window->allowCursorLock = json_is_true(allowCursorLockJ); | |||||
allowCursorLock = json_is_true(allowCursorLockJ); | |||||
// sampleRate | |||||
json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | ||||
if (sampleRateJ) { | |||||
float sampleRate = json_number_value(sampleRateJ); | |||||
APP->engine->setSampleRate(sampleRate); | |||||
} | |||||
// patchPath | |||||
json_t *patchPathJ = json_object_get(rootJ, "patchPath"); | |||||
if (patchPathJ) | |||||
APP->patch->path = json_string_value(patchPathJ); | |||||
if (sampleRateJ) | |||||
sampleRate = json_number_value(sampleRateJ); | |||||
// skipLoadOnLaunch | |||||
json_t *skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch"); | |||||
if (skipLoadOnLaunchJ) | |||||
skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ); | |||||
json_t *threadCountJ = json_object_get(rootJ, "threadCount"); | |||||
if (threadCountJ) | |||||
threadCount = json_integer_value(threadCountJ); | |||||
// moduleBrowser | |||||
json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser"); | |||||
if (moduleBrowserJ) | |||||
app::moduleBrowserFromJson(moduleBrowserJ); | |||||
json_t *paramTooltipJ = json_object_get(rootJ, "paramTooltip"); | |||||
if (paramTooltipJ) | |||||
paramTooltip = json_boolean_value(paramTooltipJ); | |||||
// powerMeter | |||||
json_t *powerMeterJ = json_object_get(rootJ, "powerMeter"); | |||||
if (powerMeterJ) | |||||
powerMeter = json_boolean_value(powerMeterJ); | |||||
json_t *cpuMeterJ = json_object_get(rootJ, "cpuMeter"); | |||||
if (cpuMeterJ) | |||||
cpuMeter = json_boolean_value(cpuMeterJ); | |||||
// threadCount | |||||
json_t *threadCountJ = json_object_get(rootJ, "threadCount"); | |||||
if (threadCountJ) | |||||
APP->engine->setThreadCount(json_integer_value(threadCountJ)); | |||||
json_t *lockModulesJ = json_object_get(rootJ, "lockModules"); | |||||
if (lockModulesJ) | |||||
lockModules = json_boolean_value(lockModulesJ); | |||||
// checkVersion | |||||
json_t *checkVersionJ = json_object_get(rootJ, "checkVersion"); | json_t *checkVersionJ = json_object_get(rootJ, "checkVersion"); | ||||
if (checkVersionJ) | if (checkVersionJ) | ||||
checkVersion = json_boolean_value(checkVersionJ); | checkVersion = json_boolean_value(checkVersionJ); | ||||
// paramTooltip | |||||
json_t *paramTooltipJ = json_object_get(rootJ, "paramTooltip"); | |||||
if (paramTooltipJ) | |||||
paramTooltip = json_boolean_value(paramTooltipJ); | |||||
// frameRateLimit | |||||
json_t *frameRateLimitJ = json_object_get(rootJ, "frameRateLimit"); | json_t *frameRateLimitJ = json_object_get(rootJ, "frameRateLimit"); | ||||
if (frameRateLimitJ) | if (frameRateLimitJ) | ||||
frameRateLimit = json_number_value(frameRateLimitJ); | frameRateLimit = json_number_value(frameRateLimitJ); | ||||
// frameRateSync | |||||
json_t *frameRateSyncJ = json_object_get(rootJ, "frameRateSync"); | json_t *frameRateSyncJ = json_object_get(rootJ, "frameRateSync"); | ||||
if (frameRateSyncJ) | if (frameRateSyncJ) | ||||
frameRateSync = json_boolean_value(frameRateSyncJ); | frameRateSync = json_boolean_value(frameRateSyncJ); | ||||
} | |||||
json_t *skipLoadOnLaunchJ = json_object_get(rootJ, "skipLoadOnLaunch"); | |||||
if (skipLoadOnLaunchJ) | |||||
skipLoadOnLaunch = json_boolean_value(skipLoadOnLaunchJ); | |||||
json_t *patchPathJ = json_object_get(rootJ, "patchPath"); | |||||
if (patchPathJ) | |||||
patchPath = json_string_value(patchPathJ); | |||||
json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser"); | |||||
if (moduleBrowserJ) | |||||
app::moduleBrowserFromJson(moduleBrowserJ); | |||||
} | |||||
void save(std::string filename) { | |||||
void Settings::save(std::string filename) { | |||||
INFO("Saving settings %s", filename.c_str()); | INFO("Saving settings %s", filename.c_str()); | ||||
json_t *rootJ = settingsToJson(); | |||||
json_t *rootJ = toJson(); | |||||
if (rootJ) { | if (rootJ) { | ||||
FILE *file = fopen(filename.c_str(), "w"); | FILE *file = fopen(filename.c_str(), "w"); | ||||
if (!file) | if (!file) | ||||
@@ -196,7 +152,7 @@ void save(std::string filename) { | |||||
} | } | ||||
} | } | ||||
void load(std::string filename) { | |||||
void Settings::load(std::string filename) { | |||||
INFO("Loading settings %s", filename.c_str()); | INFO("Loading settings %s", filename.c_str()); | ||||
FILE *file = fopen(filename.c_str(), "r"); | FILE *file = fopen(filename.c_str(), "r"); | ||||
if (!file) | if (!file) | ||||
@@ -205,7 +161,7 @@ void load(std::string filename) { | |||||
json_error_t error; | json_error_t error; | ||||
json_t *rootJ = json_loadf(file, 0, &error); | json_t *rootJ = json_loadf(file, 0, &error); | ||||
if (rootJ) { | if (rootJ) { | ||||
settingsFromJson(rootJ); | |||||
fromJson(rootJ); | |||||
json_decref(rootJ); | json_decref(rootJ); | ||||
} | } | ||||
else { | else { | ||||
@@ -216,17 +172,7 @@ void load(std::string filename) { | |||||
} | } | ||||
float zoom = 1.0; | |||||
float cableOpacity = 0.5; | |||||
float cableTension = 0.5; | |||||
bool paramTooltip = false; | |||||
bool powerMeter = false; | |||||
bool lockModules = false; | |||||
bool checkVersion = true; | |||||
bool skipLoadOnLaunch = false; | |||||
float frameRateLimit = 0.0; | |||||
bool frameRateSync = true; | |||||
Settings settings; | |||||
} // namespace settings | |||||
} // namespace rack | } // namespace rack |
@@ -12,7 +12,7 @@ | |||||
#include <queue> | #include <queue> | ||||
#include <thread> | #include <thread> | ||||
#ifdef ARCH_MAC | |||||
#if defined ARCH_MAC | |||||
// For CGAssociateMouseAndMouseCursorPosition | // For CGAssociateMouseAndMouseCursorPosition | ||||
#include <ApplicationServices/ApplicationServices.h> | #include <ApplicationServices/ApplicationServices.h> | ||||
#endif | #endif | ||||
@@ -109,7 +109,7 @@ static void windowSizeCallback(GLFWwindow *win, int width, int height) { | |||||
static void mouseButtonCallback(GLFWwindow *win, int button, int action, int mods) { | static void mouseButtonCallback(GLFWwindow *win, int button, int action, int mods) { | ||||
Window *window = (Window*) glfwGetWindowUserPointer(win); | Window *window = (Window*) glfwGetWindowUserPointer(win); | ||||
#ifdef ARCH_MAC | |||||
#if defined ARCH_MAC | |||||
// Remap Ctrl-left click to right click on Mac | // Remap Ctrl-left click to right click on Mac | ||||
if (button == GLFW_MOUSE_BUTTON_LEFT) { | if (button == GLFW_MOUSE_BUTTON_LEFT) { | ||||
if (mods & GLFW_MOD_CONTROL) { | if (mods & GLFW_MOD_CONTROL) { | ||||
@@ -130,7 +130,7 @@ static void cursorPosCallback(GLFWwindow *win, double xpos, double ypos) { | |||||
int cursorMode = glfwGetInputMode(win, GLFW_CURSOR); | int cursorMode = glfwGetInputMode(win, GLFW_CURSOR); | ||||
(void) cursorMode; | (void) cursorMode; | ||||
#ifdef ARCH_MAC | |||||
#if defined ARCH_MAC | |||||
// Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own. | // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own. | ||||
// This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped. | // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped. | ||||
if (cursorMode == GLFW_CURSOR_HIDDEN) { | if (cursorMode == GLFW_CURSOR_HIDDEN) { | ||||
@@ -223,7 +223,7 @@ Window::Window() { | |||||
glfwMakeContextCurrent(win); | glfwMakeContextCurrent(win); | ||||
// Enable v-sync | // Enable v-sync | ||||
glfwSwapInterval(settings::frameRateSync ? 1 : 0); | |||||
glfwSwapInterval(settings.frameRateSync ? 1 : 0); | |||||
glfwSetWindowSizeCallback(win, windowSizeCallback); | glfwSetWindowSizeCallback(win, windowSizeCallback); | ||||
glfwSetMouseButtonCallback(win, mouseButtonCallback); | glfwSetMouseButtonCallback(win, mouseButtonCallback); | ||||
@@ -253,6 +253,14 @@ Window::Window() { | |||||
glfwSetWindowSizeLimits(win, 800, 600, GLFW_DONT_CARE, GLFW_DONT_CARE); | glfwSetWindowSizeLimits(win, 800, 600, GLFW_DONT_CARE, GLFW_DONT_CARE); | ||||
if (settings.windowSize.isZero()) { | |||||
glfwMaximizeWindow(win); | |||||
} | |||||
else { | |||||
glfwSetWindowSize(win, settings.windowSize.x, settings.windowSize.y); | |||||
glfwSetWindowPos(win, settings.windowPos.x, settings.windowPos.y); | |||||
} | |||||
// Set up NanoVG | // Set up NanoVG | ||||
int nvgFlags = NVG_ANTIALIAS; | int nvgFlags = NVG_ANTIALIAS; | ||||
#if defined NANOVG_GL2 | #if defined NANOVG_GL2 | ||||
@@ -275,6 +283,19 @@ Window::Window() { | |||||
} | } | ||||
Window::~Window() { | Window::~Window() { | ||||
if (glfwGetWindowAttrib(win, GLFW_MAXIMIZED)) { | |||||
settings.windowSize = math::Vec(); | |||||
settings.windowPos = math::Vec(); | |||||
} | |||||
else { | |||||
int winWidth, winHeight; | |||||
glfwGetWindowSize(win, &winWidth, &winHeight); | |||||
int winX, winY; | |||||
glfwGetWindowPos(win, &winX, &winY); | |||||
settings.windowSize = math::Vec(winWidth, winHeight); | |||||
settings.windowPos = math::Vec(winX, winY); | |||||
} | |||||
#if defined NANOVG_GL2 | #if defined NANOVG_GL2 | ||||
nvgDeleteGL2(vg); | nvgDeleteGL2(vg); | ||||
#elif defined NANOVG_GL3 | #elif defined NANOVG_GL3 | ||||
@@ -375,9 +396,9 @@ void Window::run() { | |||||
// Limit frame rate | // Limit frame rate | ||||
double endTime = glfwGetTime(); | double endTime = glfwGetTime(); | ||||
if (settings::frameRateLimit > 0.0) { | |||||
if (settings.frameRateLimit > 0.0) { | |||||
double frameDuration = endTime - startTime; | double frameDuration = endTime - startTime; | ||||
double waitDuration = 1.0 / settings::frameRateLimit - frameDuration; | |||||
double waitDuration = 1.0 / settings.frameRateLimit - frameDuration; | |||||
if (waitDuration > 0.0) { | if (waitDuration > 0.0) { | ||||
std::this_thread::sleep_for(std::chrono::duration<double>(waitDuration)); | std::this_thread::sleep_for(std::chrono::duration<double>(waitDuration)); | ||||
} | } | ||||
@@ -394,8 +415,8 @@ void Window::close() { | |||||
} | } | ||||
void Window::cursorLock() { | void Window::cursorLock() { | ||||
if (allowCursorLock) { | |||||
#ifdef ARCH_MAC | |||||
if (settings.allowCursorLock) { | |||||
#if defined ARCH_MAC | |||||
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); | glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); | ||||
#else | #else | ||||
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | ||||
@@ -404,7 +425,7 @@ void Window::cursorLock() { | |||||
} | } | ||||
void Window::cursorUnlock() { | void Window::cursorUnlock() { | ||||
if (allowCursorLock) { | |||||
if (settings.allowCursorLock) { | |||||
glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | glfwSetInputMode(win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | ||||
} | } | ||||
} | } | ||||
@@ -422,34 +443,6 @@ int Window::getMods() { | |||||
return mods; | return mods; | ||||
} | } | ||||
math::Vec Window::getWindowSize() { | |||||
int width, height; | |||||
glfwGetWindowSize(win, &width, &height); | |||||
return math::Vec(width, height); | |||||
} | |||||
void Window::setWindowSize(math::Vec size) { | |||||
int width = size.x; | |||||
int height = size.y; | |||||
glfwSetWindowSize(win, width, height); | |||||
} | |||||
math::Vec Window::getWindowPos() { | |||||
int x, y; | |||||
glfwGetWindowPos(win, &x, &y); | |||||
return math::Vec(x, y); | |||||
} | |||||
void Window::setWindowPos(math::Vec pos) { | |||||
int x = pos.x; | |||||
int y = pos.y; | |||||
glfwSetWindowPos(win, x, y); | |||||
} | |||||
bool Window::isMaximized() { | |||||
return glfwGetWindowAttrib(win, GLFW_MAXIMIZED); | |||||
} | |||||
void Window::setFullScreen(bool fullScreen) { | void Window::setFullScreen(bool fullScreen) { | ||||
if (isFullScreen()) { | if (isFullScreen()) { | ||||
glfwSetWindowMonitor(win, NULL, internal->lastWindowX, internal->lastWindowY, internal->lastWindowWidth, internal->lastWindowHeight, GLFW_DONT_CARE); | glfwSetWindowMonitor(win, NULL, internal->lastWindowX, internal->lastWindowY, internal->lastWindowWidth, internal->lastWindowHeight, GLFW_DONT_CARE); | ||||