| @@ -17,6 +17,10 @@ ifeq ($(ARCH), mac) | |||
| FLAGS += -DARCH_MAC | |||
| CXXFLAGS += -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 | |||
| 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 gApplicationVersion; | |||
| extern std::string gApiHost; | |||
| extern RackWidget *gRackWidget; | |||
| @@ -8,10 +8,10 @@ namespace rack { | |||
| 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) */ | |||
| @@ -5,6 +5,7 @@ namespace rack { | |||
| std::string gApplicationName = "VCV Rack"; | |||
| std::string gApplicationVersion = TOSTRING(VERSION); | |||
| std::string gApiHost = "http://api.vcvrack.com"; | |||
| RackWidget *gRackWidget = NULL; | |||
| @@ -1,9 +1,31 @@ | |||
| #include "app.hpp" | |||
| #include "gui.hpp" | |||
| #include "util/request.hpp" | |||
| #include "../ext/osdialog/osdialog.h" | |||
| #include <thread> | |||
| 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() { | |||
| scrollWidget = new ScrollWidget(); | |||
| { | |||
| @@ -16,6 +38,12 @@ RackScene::RackScene() { | |||
| toolbar = new Toolbar(); | |||
| addChild(toolbar); | |||
| 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() { | |||
| @@ -23,6 +51,16 @@ void RackScene::step() { | |||
| scrollWidget->box.size = box.size.minus(scrollWidget->box.pos); | |||
| 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) { | |||
| @@ -3,7 +3,6 @@ | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #include <thread> | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #include <sys/param.h> // for MAXPATHLEN | |||
| @@ -22,9 +21,8 @@ | |||
| #include <dirent.h> | |||
| #include "plugin.hpp" | |||
| #include "gui.hpp" // for guiClose | |||
| #include "app.hpp" | |||
| #include "util/request.hpp" | |||
| #include "../ext/osdialog/osdialog.h" | |||
| namespace rack { | |||
| @@ -37,7 +35,6 @@ static float downloadProgress = 0.0; | |||
| static std::string downloadName; | |||
| static std::string loginStatus; | |||
| static std::string apiHost = "http://api.vcvrack.com"; | |||
| Plugin::~Plugin() { | |||
| @@ -200,37 +197,11 @@ static void refreshPurchase(json_t *pluginJ) { | |||
| 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 | |||
| //////////////////// | |||
| void pluginInit() { | |||
| if (gApplicationVersion != "dev") { | |||
| std::thread versionThread(checkVersion); | |||
| versionThread.detach(); | |||
| } | |||
| // Load core | |||
| // This function is defined in core.cpp | |||
| Plugin *corePlugin = init(); | |||
| @@ -261,7 +232,7 @@ void pluginLogIn(std::string email, std::string password) { | |||
| json_t *reqJ = json_object(); | |||
| json_object_set(reqJ, "email", json_string(email.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); | |||
| if (resJ) { | |||
| @@ -296,7 +267,7 @@ void pluginRefresh() { | |||
| json_t *reqJ = json_object(); | |||
| 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); | |||
| if (resJ) { | |||
| @@ -3,6 +3,7 @@ | |||
| #include <GL/glew.h> | |||
| #include "../ext/nanovg/src/nanovg_gl.h" | |||
| #include "../ext/nanovg/src/nanovg_gl_utils.h" | |||
| #include "../ext/osdialog/osdialog.h" | |||
| namespace rack { | |||
| @@ -39,6 +40,7 @@ void FramebufferWidget::step() { | |||
| if (dirty) { | |||
| internal->box.pos = padding.neg(); | |||
| 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); | |||
| // assert(fbSize.isFinite()); | |||
| // Reject zero area size | |||
| @@ -48,6 +50,14 @@ void FramebufferWidget::step() { | |||
| // Delete old one first to free up GPU memory | |||
| internal->setFramebuffer(NULL); | |||
| 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) | |||
| return; | |||
| internal->setFramebuffer(fb); | |||
| @@ -5,7 +5,8 @@ namespace rack { | |||
| 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; | |||
| } | |||