@@ -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; | |||||
} | } | ||||