@@ -1,6 +1,6 @@ | |||||
RACK_DIR ?= . | RACK_DIR ?= . | ||||
# VERSION := 1.dev.$(shell git rev-parse --short HEAD) | |||||
VERSION := 1.1.0 | |||||
VERSION := 1.dev.$(shell git rev-parse --short HEAD) | |||||
# VERSION := 1.1.0 | |||||
FLAGS += -DVERSION=$(VERSION) | FLAGS += -DVERSION=$(VERSION) | ||||
FLAGS += -Iinclude -Idep/include | FLAGS += -Iinclude -Idep/include | ||||
@@ -12,15 +12,17 @@ namespace rack { | |||||
namespace app { | namespace app { | ||||
extern std::string APP_NAME; | |||||
extern std::string APP_VERSION; | |||||
extern std::string APP_VERSION_UPDATE; | |||||
extern std::string API_URL; | |||||
extern std::string API_VERSION; | |||||
extern std::string ABI_VERSION; | |||||
extern const std::string APP_NAME; | |||||
extern const std::string APP_VERSION; | |||||
extern const std::string APP_ARCH; | |||||
static const float SVG_DPI = 75.0; | |||||
static const float MM_PER_IN = 25.4; | |||||
extern const std::string ABI_VERSION; | |||||
extern const std::string API_URL; | |||||
extern const std::string API_VERSION; | |||||
static const float SVG_DPI = 75.f; | |||||
static const float MM_PER_IN = 25.4f; | |||||
/** Converts inch measurements to pixels */ | /** Converts inch measurements to pixels */ | ||||
@@ -50,8 +52,5 @@ static const math::Vec RACK_OFFSET = RACK_GRID_SIZE.mult(math::Vec(2000, 100)); | |||||
static const math::Vec BUS_BOARD_GRID_SIZE = math::Vec(RACK_GRID_WIDTH * 20, RACK_GRID_HEIGHT); | static const math::Vec BUS_BOARD_GRID_SIZE = math::Vec(RACK_GRID_WIDTH * 20, RACK_GRID_HEIGHT); | ||||
void init(); | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack |
@@ -0,0 +1,26 @@ | |||||
#pragma once | |||||
#include <common.hpp> | |||||
namespace rack { | |||||
/** Automatically updates the application. | |||||
*/ | |||||
namespace updater { | |||||
extern std::string version; | |||||
extern std::string changelogUrl; | |||||
extern float progress; | |||||
void init(); | |||||
bool isUpdateAvailable(); | |||||
/** Updates Rack automatically or opens the browser to the URL. | |||||
Blocking. Call on a detached thread. | |||||
*/ | |||||
void update(); | |||||
} // namespace updater | |||||
} // namespace rack |
@@ -15,6 +15,7 @@ | |||||
#include <system.hpp> | #include <system.hpp> | ||||
#include <plugin.hpp> | #include <plugin.hpp> | ||||
#include <patch.hpp> | #include <patch.hpp> | ||||
#include <updater.hpp> | |||||
#include <osdialog.h> | #include <osdialog.h> | ||||
#include <thread> | #include <thread> | ||||
@@ -61,6 +62,16 @@ struct UrlItem : ui::MenuItem { | |||||
} | } | ||||
}; | }; | ||||
struct FolderItem : ui::MenuItem { | |||||
std::string path; | |||||
void onAction(const event::Action &e) override { | |||||
std::thread t([=] { | |||||
system::openFolder(path); | |||||
}); | |||||
t.detach(); | |||||
} | |||||
}; | |||||
//////////////////// | //////////////////// | ||||
// File | // File | ||||
//////////////////// | //////////////////// | ||||
@@ -701,15 +712,35 @@ struct LibraryButton : MenuButton { | |||||
// Help | // Help | ||||
//////////////////// | //////////////////// | ||||
struct UserFolderItem : ui::MenuItem { | |||||
struct UpdateItem : ui::MenuItem { | |||||
ui::Menu *createChildMenu() override { | |||||
ui::Menu *menu = new ui::Menu; | |||||
UrlItem *changelogUrl = new UrlItem; | |||||
changelogUrl->text = "Changelog"; | |||||
changelogUrl->url = updater::changelogUrl; | |||||
menu->addChild(changelogUrl); | |||||
return menu; | |||||
} | |||||
void step() override { | |||||
if (updater::progress > 0) { | |||||
rightText = string::f("%.0f%%", updater::progress * 100.f); | |||||
} | |||||
MenuItem::step(); | |||||
} | |||||
void onAction(const event::Action &e) override { | void onAction(const event::Action &e) override { | ||||
std::thread t([] { | |||||
system::openFolder(asset::user("")); | |||||
std::thread t([=] { | |||||
updater::update(); | |||||
}); | }); | ||||
t.detach(); | t.detach(); | ||||
e.consume(NULL); | |||||
} | } | ||||
}; | }; | ||||
struct HelpButton : MenuButton { | struct HelpButton : MenuButton { | ||||
NotificationIcon *notification; | NotificationIcon *notification; | ||||
@@ -723,11 +754,10 @@ struct HelpButton : MenuButton { | |||||
menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | ||||
menu->box.size.x = box.size.x; | menu->box.size.x = box.size.x; | ||||
if (hasUpdate()) { | |||||
UrlItem *updateItem = new UrlItem; | |||||
if (updater::isUpdateAvailable()) { | |||||
UpdateItem *updateItem = new UpdateItem; | |||||
updateItem->text = "Update " + APP_NAME; | updateItem->text = "Update " + APP_NAME; | ||||
updateItem->rightText = APP_VERSION + " → " + APP_VERSION_UPDATE; | |||||
updateItem->url = "https://vcvrack.com/"; | |||||
updateItem->rightText = APP_VERSION + " → " + updater::version; | |||||
menu->addChild(updateItem); | menu->addChild(updateItem); | ||||
} | } | ||||
@@ -742,20 +772,17 @@ struct HelpButton : MenuButton { | |||||
websiteItem->url = "https://vcvrack.com/"; | websiteItem->url = "https://vcvrack.com/"; | ||||
menu->addChild(websiteItem); | menu->addChild(websiteItem); | ||||
UserFolderItem *folderItem = new UserFolderItem; | |||||
FolderItem *folderItem = new FolderItem; | |||||
folderItem->text = "Open user folder"; | folderItem->text = "Open user folder"; | ||||
folderItem->path = asset::user(""); | |||||
menu->addChild(folderItem); | menu->addChild(folderItem); | ||||
} | } | ||||
void step() override { | void step() override { | ||||
notification->box.pos = math::Vec(0, 0); | notification->box.pos = math::Vec(0, 0); | ||||
notification->visible = hasUpdate(); | |||||
notification->visible = updater::isUpdateAvailable(); | |||||
MenuButton::step(); | MenuButton::step(); | ||||
} | } | ||||
bool hasUpdate() { | |||||
return !APP_VERSION_UPDATE.empty() && APP_VERSION_UPDATE != APP_VERSION; | |||||
} | |||||
}; | }; | ||||
//////////////////// | //////////////////// | ||||
@@ -1,45 +1,24 @@ | |||||
#include <app/common.hpp> | #include <app/common.hpp> | ||||
#include <settings.hpp> | |||||
#include <network.hpp> | |||||
#include <thread> | |||||
namespace rack { | namespace rack { | ||||
namespace app { | namespace app { | ||||
std::string APP_NAME = "VCV Rack"; | |||||
std::string APP_VERSION = TOSTRING(VERSION); | |||||
std::string APP_VERSION_UPDATE; | |||||
std::string API_URL = "https://api.vcvrack.com"; | |||||
std::string API_VERSION = "1"; | |||||
std::string ABI_VERSION = "1"; | |||||
static void checkVersion() { | |||||
std::string versionUrl = app::API_URL + "/version"; | |||||
json_t *versionResJ = network::requestJson(network::METHOD_GET, versionUrl, NULL); | |||||
if (!versionResJ) { | |||||
WARN("Request for version failed"); | |||||
return; | |||||
} | |||||
DEFER({ | |||||
json_decref(versionResJ); | |||||
}); | |||||
json_t *versionJ = json_object_get(versionResJ, "version"); | |||||
if (versionJ) | |||||
APP_VERSION_UPDATE = json_string_value(versionJ); | |||||
} | |||||
void init() { | |||||
if (!settings::devMode) { | |||||
std::thread t([] { | |||||
checkVersion(); | |||||
}); | |||||
t.detach(); | |||||
} | |||||
} | |||||
const std::string APP_NAME = "VCV Rack"; | |||||
const std::string APP_VERSION = TOSTRING(VERSION); | |||||
#if defined ARCH_WIN | |||||
const std::string APP_ARCH = "win"; | |||||
#elif ARCH_MAC | |||||
const std::string APP_ARCH = "mac"; | |||||
#elif defined ARCH_LIN | |||||
const std::string APP_ARCH = "lin"; | |||||
#endif | |||||
const std::string ABI_VERSION = "1"; | |||||
const std::string API_URL = "https://api.vcvrack.com"; | |||||
const std::string API_VERSION = "1"; | |||||
} // namespace app | } // namespace app | ||||
@@ -16,6 +16,7 @@ | |||||
#include <ui.hpp> | #include <ui.hpp> | ||||
#include <system.hpp> | #include <system.hpp> | ||||
#include <string.hpp> | #include <string.hpp> | ||||
#include <updater.hpp> | |||||
#include <osdialog.h> | #include <osdialog.h> | ||||
#include <thread> | #include <thread> | ||||
@@ -150,7 +151,7 @@ int main(int argc, char *argv[]) { | |||||
keyboard::init(); | keyboard::init(); | ||||
gamepad::init(); | gamepad::init(); | ||||
plugin::init(); | plugin::init(); | ||||
app::init(); | |||||
updater::init(); | |||||
if (!settings::headless) { | if (!settings::headless) { | ||||
ui::init(); | ui::init(); | ||||
windowInit(); | windowInit(); | ||||
@@ -476,21 +476,13 @@ void syncUpdate(Update *update) { | |||||
isSyncingUpdate = false; | isSyncingUpdate = false; | ||||
}); | }); | ||||
#if defined ARCH_WIN | |||||
std::string arch = "win"; | |||||
#elif ARCH_MAC | |||||
std::string arch = "mac"; | |||||
#elif defined ARCH_LIN | |||||
std::string arch = "lin"; | |||||
#endif | |||||
std::string downloadUrl = app::API_URL + "/download"; | std::string downloadUrl = app::API_URL + "/download"; | ||||
downloadUrl += "?token=" + network::encodeUrl(settings::token); | downloadUrl += "?token=" + network::encodeUrl(settings::token); | ||||
downloadUrl += "&slug=" + network::encodeUrl(update->pluginSlug); | downloadUrl += "&slug=" + network::encodeUrl(update->pluginSlug); | ||||
downloadUrl += "&version=" + network::encodeUrl(update->version); | downloadUrl += "&version=" + network::encodeUrl(update->version); | ||||
downloadUrl += "&arch=" + network::encodeUrl(arch); | |||||
downloadUrl += "&arch=" + network::encodeUrl(app::APP_ARCH); | |||||
INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), arch.c_str()); | |||||
INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), app::APP_ARCH.c_str()); | |||||
// Download zip | // Download zip | ||||
std::string pluginDest = asset::pluginsPath + "/" + update->pluginSlug + ".zip"; | std::string pluginDest = asset::pluginsPath + "/" + update->pluginSlug + ".zip"; | ||||
@@ -0,0 +1,83 @@ | |||||
#include <updater.hpp> | |||||
#include <settings.hpp> | |||||
#include <app/common.hpp> | |||||
#include <network.hpp> | |||||
#include <system.hpp> | |||||
#include <app.hpp> | |||||
#include <window.hpp> | |||||
#include <thread> | |||||
namespace rack { | |||||
namespace updater { | |||||
std::string version; | |||||
std::string changelogUrl; | |||||
float progress = 0.f; | |||||
static std::string downloadUrl; | |||||
static void checkVersion() { | |||||
std::string versionUrl = app::API_URL + "/version"; | |||||
json_t *resJ = network::requestJson(network::METHOD_GET, versionUrl, NULL); | |||||
if (!resJ) { | |||||
WARN("Request for version failed"); | |||||
return; | |||||
} | |||||
DEFER({ | |||||
json_decref(resJ); | |||||
}); | |||||
json_t *versionJ = json_object_get(resJ, "version"); | |||||
if (versionJ) | |||||
version = json_string_value(versionJ); | |||||
json_t *changelogUrlJ = json_object_get(resJ, "changelogUrl"); | |||||
if (changelogUrlJ) | |||||
changelogUrl = json_string_value(changelogUrlJ); | |||||
json_t *downloadUrlsJ = json_object_get(resJ, "downloadUrls"); | |||||
if (downloadUrlsJ) { | |||||
json_t *downloadUrlJ = json_object_get(downloadUrlsJ, app::APP_ARCH.c_str()); | |||||
if (downloadUrlJ) | |||||
downloadUrl = json_string_value(downloadUrlJ); | |||||
} | |||||
} | |||||
void init() { | |||||
if (!settings::devMode) { | |||||
std::thread t([] { | |||||
checkVersion(); | |||||
}); | |||||
t.detach(); | |||||
} | |||||
} | |||||
void update() { | |||||
if (downloadUrl == "") | |||||
return; | |||||
#if defined ARCH_WIN | |||||
// Download and launch the installer on Windows | |||||
std::string path = asset::user("Rack-setup.exe"); | |||||
network::requestDownload(downloadUrl, path, &progress); | |||||
system::runProcessDetached(path); | |||||
#else | |||||
// Open the browser on Mac and Linux. The user will know what to do. | |||||
system::openBrowser(downloadUrl); | |||||
#endif | |||||
APP->window->close(); | |||||
} | |||||
bool isUpdateAvailable() { | |||||
return (version != "") && (version != app::APP_VERSION); | |||||
} | |||||
} // namespace updater | |||||
} // namespace rack |