| @@ -17,6 +17,10 @@ ifeq ($(ARCH), mac) | |||||
| FLAGS += -DARCH_MAC | FLAGS += -DARCH_MAC | ||||
| CXXFLAGS += -stdlib=libc++ | CXXFLAGS += -stdlib=libc++ | ||||
| LDFLAGS += -stdlib=libc++ | LDFLAGS += -stdlib=libc++ | ||||
| MAC_SDK_FLAGS = -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk \ | |||||
| -mmacosx-version-min=10.7 | |||||
| FLAGS += $(MAC_SDK_FLAGS) | |||||
| LDFLAGS += $(MAC_SDK_FLAGS) | |||||
| endif | endif | ||||
| ifeq ($(ARCH), win) | ifeq ($(ARCH), win) | ||||
| @@ -1 +1 @@ | |||||
| Subproject commit 952d72f925ea5e9086067d47a9d31516878fcaac | |||||
| Subproject commit c36d25ad244f81cf9f6d27df811cd58f434e7d75 | |||||
| @@ -314,6 +314,7 @@ struct RackScene : Scene { | |||||
| extern std::string gApplicationName; | extern std::string gApplicationName; | ||||
| extern std::string gApplicationVersion; | extern std::string gApplicationVersion; | ||||
| extern std::string gApiHost; | |||||
| extern RackWidget *gRackWidget; | extern RackWidget *gRackWidget; | ||||
| @@ -8,10 +8,10 @@ namespace rack { | |||||
| enum RequestMethod { | enum RequestMethod { | ||||
| GET_METHOD, | |||||
| POST_METHOD, | |||||
| PUT_METHOD, | |||||
| DELETE_METHOD, | |||||
| METHOD_GET, | |||||
| METHOD_POST, | |||||
| METHOD_PUT, | |||||
| 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) */ | ||||
| @@ -5,6 +5,7 @@ namespace rack { | |||||
| std::string gApplicationName = "VCV Rack"; | std::string gApplicationName = "VCV Rack"; | ||||
| std::string gApplicationVersion = TOSTRING(VERSION); | std::string gApplicationVersion = TOSTRING(VERSION); | ||||
| std::string gApiHost = "http://api.vcvrack.com"; | |||||
| RackWidget *gRackWidget = NULL; | RackWidget *gRackWidget = NULL; | ||||
| @@ -1,9 +1,31 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "gui.hpp" | #include "gui.hpp" | ||||
| #include "util/request.hpp" | |||||
| #include "../ext/osdialog/osdialog.h" | |||||
| #include <thread> | |||||
| namespace rack { | namespace rack { | ||||
| static std::string versionMessage = ""; | |||||
| 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) { | |||||
| versionMessage = stringf("Rack %s is available.\n\nYou have Rack %s.\n\nWould you like to download the new version on the website?", version, gApplicationVersion.c_str()); | |||||
| } | |||||
| } | |||||
| json_decref(resJ); | |||||
| } | |||||
| } | |||||
| RackScene::RackScene() { | RackScene::RackScene() { | ||||
| scrollWidget = new ScrollWidget(); | scrollWidget = new ScrollWidget(); | ||||
| { | { | ||||
| @@ -16,6 +38,12 @@ RackScene::RackScene() { | |||||
| toolbar = new Toolbar(); | toolbar = new Toolbar(); | ||||
| addChild(toolbar); | addChild(toolbar); | ||||
| scrollWidget->box.pos.y = toolbar->box.size.y; | scrollWidget->box.pos.y = toolbar->box.size.y; | ||||
| // Check for new version | |||||
| if (gApplicationVersion != "dev" || true) { | |||||
| std::thread versionThread(checkVersion); | |||||
| versionThread.detach(); | |||||
| } | |||||
| } | } | ||||
| void RackScene::step() { | void RackScene::step() { | ||||
| @@ -23,6 +51,16 @@ void RackScene::step() { | |||||
| scrollWidget->box.size = box.size.minus(scrollWidget->box.pos); | scrollWidget->box.size = box.size.minus(scrollWidget->box.pos); | ||||
| Scene::step(); | Scene::step(); | ||||
| // Version popup message | |||||
| if (!versionMessage.empty()) { | |||||
| if (osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, versionMessage.c_str())) { | |||||
| std::thread t(openBrowser, "https://vcvrack.com/"); | |||||
| t.detach(); | |||||
| guiClose(); | |||||
| } | |||||
| versionMessage = ""; | |||||
| } | |||||
| } | } | ||||
| void RackScene::draw(NVGcontext *vg) { | void RackScene::draw(NVGcontext *vg) { | ||||
| @@ -3,7 +3,6 @@ | |||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <thread> | |||||
| #include <sys/types.h> | #include <sys/types.h> | ||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||
| #include <sys/param.h> // for MAXPATHLEN | #include <sys/param.h> // for MAXPATHLEN | ||||
| @@ -22,9 +21,8 @@ | |||||
| #include <dirent.h> | #include <dirent.h> | ||||
| #include "plugin.hpp" | #include "plugin.hpp" | ||||
| #include "gui.hpp" // for guiClose | |||||
| #include "app.hpp" | |||||
| #include "util/request.hpp" | #include "util/request.hpp" | ||||
| #include "../ext/osdialog/osdialog.h" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -37,7 +35,6 @@ static float downloadProgress = 0.0; | |||||
| static std::string downloadName; | static std::string downloadName; | ||||
| static std::string loginStatus; | static std::string loginStatus; | ||||
| static std::string apiHost = "http://api.vcvrack.com"; | |||||
| Plugin::~Plugin() { | Plugin::~Plugin() { | ||||
| @@ -200,37 +197,11 @@ static void refreshPurchase(json_t *pluginJ) { | |||||
| downloadName = ""; | downloadName = ""; | ||||
| } | } | ||||
| static void checkVersion() { | |||||
| json_t *resJ = requestJson(GET_METHOD, apiHost + "/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) { | |||||
| char text[1024]; | |||||
| snprintf(text, sizeof(text), "Rack %s is available.\n\nYou have Rack %s.\n\nWould you like to download the new version on the website?", version, gApplicationVersion.c_str()); | |||||
| if (osdialog_message(OSDIALOG_INFO, OSDIALOG_YES_NO, text)) { | |||||
| std::thread t(openBrowser, "https://vcvrack.com/"); | |||||
| t.detach(); | |||||
| guiClose(); | |||||
| } | |||||
| } | |||||
| } | |||||
| json_decref(resJ); | |||||
| } | |||||
| } | |||||
| //////////////////// | //////////////////// | ||||
| // plugin API | // plugin API | ||||
| //////////////////// | //////////////////// | ||||
| void pluginInit() { | void pluginInit() { | ||||
| if (gApplicationVersion != "dev") { | |||||
| std::thread versionThread(checkVersion); | |||||
| versionThread.detach(); | |||||
| } | |||||
| // Load core | // Load core | ||||
| // This function is defined in core.cpp | // This function is defined in core.cpp | ||||
| Plugin *corePlugin = init(); | Plugin *corePlugin = init(); | ||||
| @@ -261,7 +232,7 @@ void pluginLogIn(std::string email, std::string password) { | |||||
| json_t *reqJ = json_object(); | json_t *reqJ = json_object(); | ||||
| json_object_set(reqJ, "email", json_string(email.c_str())); | json_object_set(reqJ, "email", json_string(email.c_str())); | ||||
| json_object_set(reqJ, "password", json_string(password.c_str())); | json_object_set(reqJ, "password", json_string(password.c_str())); | ||||
| json_t *resJ = requestJson(POST_METHOD, apiHost + "/token", reqJ); | |||||
| json_t *resJ = requestJson(METHOD_POST, gApiHost + "/token", reqJ); | |||||
| json_decref(reqJ); | json_decref(reqJ); | ||||
| if (resJ) { | if (resJ) { | ||||
| @@ -296,7 +267,7 @@ void pluginRefresh() { | |||||
| json_t *reqJ = json_object(); | json_t *reqJ = json_object(); | ||||
| json_object_set(reqJ, "token", json_string(gToken.c_str())); | json_object_set(reqJ, "token", json_string(gToken.c_str())); | ||||
| json_t *resJ = requestJson(GET_METHOD, apiHost + "/purchases", reqJ); | |||||
| json_t *resJ = requestJson(METHOD_GET, gApiHost + "/purchases", reqJ); | |||||
| json_decref(reqJ); | json_decref(reqJ); | ||||
| if (resJ) { | if (resJ) { | ||||
| @@ -3,6 +3,7 @@ | |||||
| #include <GL/glew.h> | #include <GL/glew.h> | ||||
| #include "../ext/nanovg/src/nanovg_gl.h" | #include "../ext/nanovg/src/nanovg_gl.h" | ||||
| #include "../ext/nanovg/src/nanovg_gl_utils.h" | #include "../ext/nanovg/src/nanovg_gl_utils.h" | ||||
| #include "../ext/osdialog/osdialog.h" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -39,6 +40,7 @@ void FramebufferWidget::step() { | |||||
| if (dirty) { | if (dirty) { | ||||
| internal->box.pos = padding.neg(); | internal->box.pos = padding.neg(); | ||||
| internal->box.size = box.size.plus(padding.mult(2)); | internal->box.size = box.size.plus(padding.mult(2)); | ||||
| 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 | ||||
| @@ -48,6 +50,14 @@ void FramebufferWidget::step() { | |||||
| // Delete old one first to free up GPU memory | // Delete old one first to free up GPU memory | ||||
| internal->setFramebuffer(NULL); | internal->setFramebuffer(NULL); | ||||
| NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, fbSize.x, fbSize.y, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | NVGLUframebuffer *fb = nvgluCreateFramebuffer(gVg, fbSize.x, fbSize.y, NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | ||||
| if (!fb) { | |||||
| char buf[1024]; | |||||
| snprintf(buf, sizeof(buf), "%f %f, %f %f; %f %f; %p\n", internal->box.pos.x, internal->box.pos.y, internal->box.size.x, internal->box.size.y, fbSize.x, fbSize.y, fb); | |||||
| if (!osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK_CANCEL, buf)) | |||||
| exit(0); | |||||
| } | |||||
| if (!fb) | if (!fb) | ||||
| return; | return; | ||||
| internal->setFramebuffer(fb); | internal->setFramebuffer(fb); | ||||
| @@ -5,7 +5,8 @@ namespace rack { | |||||
| float MenuEntry::computeMinWidth(NVGcontext *vg) { | float MenuEntry::computeMinWidth(NVGcontext *vg) { | ||||
| return bndLabelWidth(vg, -1, text.c_str()); | |||||
| // Add 10 more pixels because Retina measurements are sometimes too small | |||||
| return bndLabelWidth(vg, -1, text.c_str()) + 10.0; | |||||
| } | } | ||||