@@ -534,13 +534,15 @@ struct RackScene : Scene { | |||||
extern std::string gApplicationName; | extern std::string gApplicationName; | ||||
extern std::string gApplicationVersion; | extern std::string gApplicationVersion; | ||||
extern std::string gApiHost; | extern std::string gApiHost; | ||||
extern std::string gLatestVersion; | |||||
extern bool gCheckVersion; | |||||
// Easy access to "singleton" widgets | // Easy access to "singleton" widgets | ||||
extern RackScene *gRackScene; | extern RackScene *gRackScene; | ||||
extern RackWidget *gRackWidget; | extern RackWidget *gRackWidget; | ||||
extern Toolbar *gToolbar; | extern Toolbar *gToolbar; | ||||
void appInit(); | |||||
void appInit(bool devMode); | |||||
void appDestroy(); | void appDestroy(); | ||||
void appModuleBrowserCreate(); | void appModuleBrowserCreate(); | ||||
json_t *appModuleBrowserToJson(); | json_t *appModuleBrowserToJson(); | ||||
@@ -6,11 +6,10 @@ | |||||
namespace rack { | namespace rack { | ||||
extern bool gSkipAutosaveOnLaunch; | |||||
void settingsSave(std::string filename); | void settingsSave(std::string filename); | ||||
void settingsLoad(std::string filename); | void settingsLoad(std::string filename); | ||||
extern bool skipAutosaveOnLaunch; | |||||
} // namespace rack | } // namespace rack |
@@ -13,9 +13,11 @@ enum RequestMethod { | |||||
METHOD_DELETE, | METHOD_DELETE, | ||||
}; | }; | ||||
/** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc) */ | |||||
/** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc) | |||||
Caller must json_decref(). | |||||
*/ | |||||
json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ); | json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ); | ||||
/** Returns the filename, blank if unsuccessful */ | |||||
/** Returns true if downloaded successfully */ | |||||
bool requestDownload(std::string url, std::string filename, float *progress); | bool requestDownload(std::string url, std::string filename, float *progress); | ||||
/** URL-encodes `s` */ | /** URL-encodes `s` */ | ||||
std::string requestEscape(std::string s); | std::string requestEscape(std::string s); | ||||
@@ -1,6 +1,7 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
#include "util/request.hpp" | #include "util/request.hpp" | ||||
#include "osdialog.h" | |||||
#include <string.h> | #include <string.h> | ||||
#include <thread> | #include <thread> | ||||
@@ -40,6 +41,17 @@ void RackScene::step() { | |||||
Scene::step(); | Scene::step(); | ||||
zoomWidget->box.size = gRackWidget->box.size.mult(zoomWidget->zoom); | zoomWidget->box.size = gRackWidget->box.size.mult(zoomWidget->zoom); | ||||
// Version popup message | |||||
if (!gLatestVersion.empty()) { | |||||
std::string versionMessage = stringf("Rack %s is available.\n\nYou have Rack %s.\n\nClose Rack and download new version on the website?", gLatestVersion.c_str(), gApplicationVersion.c_str()); | |||||
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { | |||||
std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | |||||
t.detach(); | |||||
windowClose(); | |||||
} | |||||
gLatestVersion = ""; | |||||
} | |||||
} | } | ||||
void RackScene::draw(NVGcontext *vg) { | void RackScene::draw(NVGcontext *vg) { | ||||
@@ -1,23 +1,49 @@ | |||||
#include "app.hpp" | #include "app.hpp" | ||||
#include "util/request.hpp" | |||||
#include <thread> | |||||
namespace rack { | namespace rack { | ||||
bool gDev = false; | |||||
std::string gApplicationName = "VCV Rack"; | std::string gApplicationName = "VCV Rack"; | ||||
std::string gApplicationVersion = TOSTRING(VERSION); | std::string gApplicationVersion = TOSTRING(VERSION); | ||||
std::string gApiHost = "https://api.vcvrack.com"; | std::string gApiHost = "https://api.vcvrack.com"; | ||||
// std::string gApiHost = "http://localhost:8081"; | // std::string gApiHost = "http://localhost:8081"; | ||||
std::string gLatestVersion; | |||||
bool gCheckVersion = true; | |||||
RackWidget *gRackWidget = NULL; | RackWidget *gRackWidget = NULL; | ||||
Toolbar *gToolbar = NULL; | Toolbar *gToolbar = NULL; | ||||
RackScene *gRackScene = NULL; | RackScene *gRackScene = NULL; | ||||
void appInit() { | |||||
static void checkVersion() { | |||||
json_t *resJ = requestJson(METHOD_GET, gApiHost + "/version", NULL); | |||||
if (resJ) { | |||||
json_t *versionJ = json_object_get(resJ, "version"); | |||||
if (versionJ) { | |||||
const char *version = json_string_value(versionJ); | |||||
if (version && version != gApplicationVersion) { | |||||
gLatestVersion = version; | |||||
} | |||||
} | |||||
json_decref(resJ); | |||||
} | |||||
} | |||||
void appInit(bool devMode) { | |||||
gRackScene = new RackScene(); | gRackScene = new RackScene(); | ||||
gScene = gRackScene; | gScene = gRackScene; | ||||
// Request latest version from server | |||||
if (!devMode && gCheckVersion) { | |||||
std::thread t(checkVersion); | |||||
t.detach(); | |||||
} | |||||
} | } | ||||
void appDestroy() { | void appDestroy() { | ||||
@@ -58,16 +58,16 @@ int main(int argc, char* argv[]) { | |||||
keyboardInit(); | keyboardInit(); | ||||
gamepadInit(); | gamepadInit(); | ||||
windowInit(); | windowInit(); | ||||
appInit(); | |||||
appInit(devMode); | |||||
settingsLoad(assetLocal("settings.json")); | settingsLoad(assetLocal("settings.json")); | ||||
if (patchFile.empty()) { | if (patchFile.empty()) { | ||||
std::string oldLastPath = gRackWidget->lastPath; | std::string oldLastPath = gRackWidget->lastPath; | ||||
// 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 oldSkipAutosaveOnLaunch = skipAutosaveOnLaunch; | |||||
skipAutosaveOnLaunch = true; | |||||
bool oldSkipAutosaveOnLaunch = gSkipAutosaveOnLaunch; | |||||
gSkipAutosaveOnLaunch = true; | |||||
settingsSave(assetLocal("settings.json")); | settingsSave(assetLocal("settings.json")); | ||||
skipAutosaveOnLaunch = false; | |||||
gSkipAutosaveOnLaunch = false; | |||||
if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Would you like to clear your patch and start over?")) { | if (oldSkipAutosaveOnLaunch && osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, "Rack has recovered from a crash, possibly caused by a faulty module in your patch. Would you like to clear your patch and start over?")) { | ||||
// Do nothing. Empty patch is already loaded. | // Do nothing. Empty patch is already loaded. | ||||
} | } | ||||
@@ -9,7 +9,7 @@ | |||||
namespace rack { | namespace rack { | ||||
bool skipAutosaveOnLaunch = false; | |||||
bool gSkipAutosaveOnLaunch = false; | |||||
static json_t *settingsToJson() { | static json_t *settingsToJson() { | ||||
@@ -60,7 +60,7 @@ static json_t *settingsToJson() { | |||||
json_object_set_new(rootJ, "lastPath", lastPathJ); | json_object_set_new(rootJ, "lastPath", lastPathJ); | ||||
// skipAutosaveOnLaunch | // skipAutosaveOnLaunch | ||||
if (skipAutosaveOnLaunch) { | |||||
if (gSkipAutosaveOnLaunch) { | |||||
json_object_set_new(rootJ, "skipAutosaveOnLaunch", json_true()); | json_object_set_new(rootJ, "skipAutosaveOnLaunch", json_true()); | ||||
} | } | ||||
@@ -70,6 +70,9 @@ static json_t *settingsToJson() { | |||||
// powerMeter | // powerMeter | ||||
json_object_set_new(rootJ, "powerMeter", json_boolean(gPowerMeter)); | json_object_set_new(rootJ, "powerMeter", json_boolean(gPowerMeter)); | ||||
// checkVersion | |||||
json_object_set_new(rootJ, "checkVersion", json_boolean(gCheckVersion)); | |||||
return rootJ; | return rootJ; | ||||
} | } | ||||
@@ -132,7 +135,7 @@ static void settingsFromJson(json_t *rootJ) { | |||||
// skipAutosaveOnLaunch | // skipAutosaveOnLaunch | ||||
json_t *skipAutosaveOnLaunchJ = json_object_get(rootJ, "skipAutosaveOnLaunch"); | json_t *skipAutosaveOnLaunchJ = json_object_get(rootJ, "skipAutosaveOnLaunch"); | ||||
if (skipAutosaveOnLaunchJ) | if (skipAutosaveOnLaunchJ) | ||||
skipAutosaveOnLaunch = json_boolean_value(skipAutosaveOnLaunchJ); | |||||
gSkipAutosaveOnLaunch = json_boolean_value(skipAutosaveOnLaunchJ); | |||||
// moduleBrowser | // moduleBrowser | ||||
json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser"); | json_t *moduleBrowserJ = json_object_get(rootJ, "moduleBrowser"); | ||||
@@ -143,6 +146,11 @@ static void settingsFromJson(json_t *rootJ) { | |||||
json_t *powerMeterJ = json_object_get(rootJ, "powerMeter"); | json_t *powerMeterJ = json_object_get(rootJ, "powerMeter"); | ||||
if (powerMeterJ) | if (powerMeterJ) | ||||
gPowerMeter = json_boolean_value(powerMeterJ); | gPowerMeter = json_boolean_value(powerMeterJ); | ||||
// checkVersion | |||||
json_t *checkVersionJ = json_object_get(rootJ, "checkVersion"); | |||||
if (checkVersionJ) | |||||
gCheckVersion = json_boolean_value(checkVersionJ); | |||||
} | } | ||||
@@ -115,6 +115,9 @@ static int xferInfoCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, | |||||
} | } | ||||
bool requestDownload(std::string url, std::string filename, float *progress) { | bool requestDownload(std::string url, std::string filename, float *progress) { | ||||
if (progress) | |||||
*progress = 0.f; | |||||
CURL *curl = curl_easy_init(); | CURL *curl = curl_easy_init(); | ||||
if (!curl) | if (!curl) | ||||
return false; | return false; | ||||