| @@ -1,7 +1,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "util/common.hpp" | #include "util/common.hpp" | ||||
| #include "string.hpp" | |||||
| #include "nanovg.h" | #include "nanovg.h" | ||||
| @@ -87,9 +87,9 @@ inline std::string toHexString(NVGcolor c) { | |||||
| uint8_t b = std::round(c.b * 255); | uint8_t b = std::round(c.b * 255); | ||||
| uint8_t a = std::round(c.a * 255); | uint8_t a = std::round(c.a * 255); | ||||
| if (a == 255) | if (a == 255) | ||||
| return stringf("#%02x%02x%02x", r, g, b); | |||||
| return string::stringf("#%02x%02x%02x", r, g, b); | |||||
| else | else | ||||
| return stringf("#%02x%02x%02x%02x", r, g, b, a); | |||||
| return string::stringf("#%02x%02x%02x%02x", r, g, b, a); | |||||
| } | } | ||||
| @@ -0,0 +1,40 @@ | |||||
| #pragma once | |||||
| /** Example usage: | |||||
| debug("error: %d", errno); | |||||
| will print something like | |||||
| [0.123 debug myfile.cpp:45] error: 67 | |||||
| */ | |||||
| #define DEBUG(format, ...) rack::logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define INFO(format, ...) rack::logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define WARN(format, ...) rack::logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define FATAL(format, ...) rack::logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| /** Deprecated lowercase log functions */ | |||||
| #define debug(...) DEBUG(__VA_ARGS__) | |||||
| #define info(...) INFO(__VA_ARGS__) | |||||
| #define warn(...) WARN(__VA_ARGS__) | |||||
| #define fatal(...) FATAL(__VA_ARGS__) | |||||
| namespace rack { | |||||
| namespace logger { | |||||
| enum Level { | |||||
| DEBUG_LEVEL, | |||||
| INFO_LEVEL, | |||||
| WARN_LEVEL, | |||||
| FATAL_LEVEL | |||||
| }; | |||||
| void init(bool devMode); | |||||
| void destroy(); | |||||
| /** Do not use this function directly. Use the macros below. */ | |||||
| void log(Level level, const char *filename, int line, const char *format, ...); | |||||
| } // namespace logger | |||||
| } // namespace rack | |||||
| @@ -0,0 +1,67 @@ | |||||
| #pragma once | |||||
| /** Concatenates two literals or two macros | |||||
| Example: | |||||
| #define COUNT 42 | |||||
| CONCAT(myVariable, COUNT) | |||||
| expands to | |||||
| myVariable42 | |||||
| */ | |||||
| #define CONCAT_LITERAL(x, y) x ## y | |||||
| #define CONCAT(x, y) CONCAT_LITERAL(x, y) | |||||
| /** Surrounds raw text with quotes | |||||
| Example: | |||||
| #define NAME "world" | |||||
| printf("Hello " TOSTRING(NAME)) | |||||
| expands to | |||||
| printf("Hello " "world") | |||||
| and of course the C++ lexer/parser then concatenates the string literals. | |||||
| */ | |||||
| #define TOSTRING_LITERAL(x) #x | |||||
| #define TOSTRING(x) TOSTRING_LITERAL(x) | |||||
| /** Produces the length of a static array in number of elements */ | |||||
| #define LENGTHOF(arr) (sizeof(arr) / sizeof((arr)[0])) | |||||
| /** Reserve space for `count` enums starting with `name`. | |||||
| Example: | |||||
| enum Foo { | |||||
| ENUMS(BAR, 14), | |||||
| BAZ | |||||
| }; | |||||
| BAR + 0 to BAR + 13 is reserved. BAZ has a value of 14. | |||||
| */ | |||||
| #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 | |||||
| /** Deprecation notice for GCC */ | |||||
| #define DEPRECATED __attribute__ ((deprecated)) | |||||
| /** References binary files compiled into the program. | |||||
| For example, to include a file "Test.dat" directly into your program binary, add | |||||
| BINARIES += Test.dat | |||||
| to your Makefile and declare | |||||
| BINARY(Test_dat); | |||||
| at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | |||||
| BINARY_START(Test_dat) | |||||
| BINARY_END(Test_dat) | |||||
| to reference the data beginning and end as a void* array, and | |||||
| BINARY_SIZE(Test_dat) | |||||
| to get its size in bytes. | |||||
| */ | |||||
| #ifdef ARCH_MAC | |||||
| // Use output from `xxd -i` | |||||
| #define BINARY(sym) extern unsigned char sym[]; extern unsigned int sym##_len | |||||
| #define BINARY_START(sym) ((const void*) sym) | |||||
| #define BINARY_END(sym) ((const void*) sym + sym##_len) | |||||
| #define BINARY_SIZE(sym) (sym##_len) | |||||
| #else | |||||
| #define BINARY(sym) extern char _binary_##sym##_start, _binary_##sym##_end, _binary_##sym##_size | |||||
| #define BINARY_START(sym) ((const void*) &_binary_##sym##_start) | |||||
| #define BINARY_END(sym) ((const void*) &_binary_##sym##_end) | |||||
| // The symbol "_binary_##sym##_size" doesn't seem to be valid after a plugin is dynamically loaded, so simply take the difference between the two addresses. | |||||
| #define BINARY_SIZE(sym) ((size_t) (&_binary_##sym##_end - &_binary_##sym##_start)) | |||||
| #endif | |||||
| @@ -1,8 +1,8 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "util/common.hpp" | |||||
| #include <cmath> | #include <cmath> | ||||
| #include <cstdlib> | #include <cstdlib> | ||||
| #include <algorithm> // for std::min, max | #include <algorithm> // for std::min, max | ||||
| #include "macros.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -15,6 +15,8 @@ namespace rack { | |||||
| using namespace math; | using namespace math; | ||||
| using namespace string; | |||||
| using namespace logger; | |||||
| } // namespace rack | } // namespace rack | ||||
| @@ -1,13 +1,16 @@ | |||||
| #pragma once | |||||
| #include "util/common.hpp" | #include "util/common.hpp" | ||||
| #include <stdarg.h> | |||||
| #include <algorithm> | |||||
| #include <cstdarg> | |||||
| #include <libgen.h> // for dirname and basename | #include <libgen.h> // for dirname and basename | ||||
| namespace rack { | namespace rack { | ||||
| namespace string { | |||||
| std::string stringf(const char *format, ...) { | |||||
| /** Converts a printf format string and optional arguments into a std::string */ | |||||
| inline std::string stringf(const char *format, ...) { | |||||
| va_list args; | va_list args; | ||||
| va_start(args, format); | va_start(args, format); | ||||
| // Compute size of required buffer | // Compute size of required buffer | ||||
| @@ -24,51 +27,60 @@ std::string stringf(const char *format, ...) { | |||||
| return s; | return s; | ||||
| } | } | ||||
| std::string stringLowercase(std::string s) { | |||||
| inline std::string lowercase(std::string s) { | |||||
| std::transform(s.begin(), s.end(), s.begin(), ::tolower); | std::transform(s.begin(), s.end(), s.begin(), ::tolower); | ||||
| return s; | return s; | ||||
| } | } | ||||
| std::string stringUppercase(std::string s) { | |||||
| inline std::string uppercase(std::string s) { | |||||
| std::transform(s.begin(), s.end(), s.begin(), ::toupper); | std::transform(s.begin(), s.end(), s.begin(), ::toupper); | ||||
| return s; | return s; | ||||
| } | } | ||||
| std::string stringEllipsize(std::string s, size_t len) { | |||||
| /** Truncates and adds "..." to a string, not exceeding `len` characters */ | |||||
| inline std::string ellipsize(std::string s, size_t len) { | |||||
| if (s.size() <= len) | if (s.size() <= len) | ||||
| return s; | return s; | ||||
| else | else | ||||
| return s.substr(0, len - 3) + "..."; | return s.substr(0, len - 3) + "..."; | ||||
| } | } | ||||
| bool stringStartsWith(std::string str, std::string prefix) { | |||||
| inline bool startsWith(std::string str, std::string prefix) { | |||||
| return str.substr(0, prefix.size()) == prefix; | return str.substr(0, prefix.size()) == prefix; | ||||
| } | } | ||||
| bool stringEndsWith(std::string str, std::string suffix) { | |||||
| inline bool endsWith(std::string str, std::string suffix) { | |||||
| return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; | return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; | ||||
| } | } | ||||
| std::string stringDirectory(std::string path) { | |||||
| /** Extracts portions of a path */ | |||||
| inline std::string directory(std::string path) { | |||||
| char *pathDup = strdup(path.c_str()); | char *pathDup = strdup(path.c_str()); | ||||
| std::string directory = dirname(pathDup); | std::string directory = dirname(pathDup); | ||||
| free(pathDup); | free(pathDup); | ||||
| return directory; | return directory; | ||||
| } | } | ||||
| std::string stringFilename(std::string path) { | |||||
| inline std::string filename(std::string path) { | |||||
| char *pathDup = strdup(path.c_str()); | char *pathDup = strdup(path.c_str()); | ||||
| std::string filename = basename(pathDup); | std::string filename = basename(pathDup); | ||||
| free(pathDup); | free(pathDup); | ||||
| return filename; | return filename; | ||||
| } | } | ||||
| std::string stringExtension(std::string path) { | |||||
| const char *ext = strrchr(stringFilename(path).c_str(), '.'); | |||||
| inline std::string extension(std::string path) { | |||||
| const char *ext = strrchr(filename(path).c_str(), '.'); | |||||
| if (!ext) | if (!ext) | ||||
| return ""; | return ""; | ||||
| return ext + 1; | return ext + 1; | ||||
| } | } | ||||
| struct CaseInsensitiveCompare { | |||||
| bool operator()(const std::string &a, const std::string &b) const { | |||||
| return lowercase(a) < lowercase(b); | |||||
| } | |||||
| }; | |||||
| } // namespace string | |||||
| } // namespace rack | } // namespace rack | ||||
| @@ -13,77 +13,10 @@ | |||||
| #include <condition_variable> | #include <condition_variable> | ||||
| #include <mutex> | #include <mutex> | ||||
| //////////////////// | |||||
| // Handy macros | |||||
| //////////////////// | |||||
| /** Concatenates two literals or two macros | |||||
| Example: | |||||
| #define COUNT 42 | |||||
| CONCAT(myVariable, COUNT) | |||||
| expands to | |||||
| myVariable42 | |||||
| */ | |||||
| #define CONCAT_LITERAL(x, y) x ## y | |||||
| #define CONCAT(x, y) CONCAT_LITERAL(x, y) | |||||
| /** Surrounds raw text with quotes | |||||
| Example: | |||||
| #define NAME "world" | |||||
| printf("Hello " TOSTRING(NAME)) | |||||
| expands to | |||||
| printf("Hello " "world") | |||||
| and of course the C++ lexer/parser then concatenates the string literals. | |||||
| */ | |||||
| #define TOSTRING_LITERAL(x) #x | |||||
| #define TOSTRING(x) TOSTRING_LITERAL(x) | |||||
| /** Produces the length of a static array in number of elements */ | |||||
| #define LENGTHOF(arr) (sizeof(arr) / sizeof((arr)[0])) | |||||
| /** Reserve space for `count` enums starting with `name`. | |||||
| Example: | |||||
| enum Foo { | |||||
| ENUMS(BAR, 14), | |||||
| BAZ | |||||
| }; | |||||
| BAR + 0 to BAR + 13 is reserved. BAZ has a value of 14. | |||||
| */ | |||||
| #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 | |||||
| /** Deprecation notice for GCC */ | |||||
| #define DEPRECATED __attribute__ ((deprecated)) | |||||
| /** References binary files compiled into the program. | |||||
| For example, to include a file "Test.dat" directly into your program binary, add | |||||
| BINARIES += Test.dat | |||||
| to your Makefile and declare | |||||
| BINARY(Test_dat); | |||||
| at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | |||||
| BINARY_START(Test_dat) | |||||
| BINARY_END(Test_dat) | |||||
| to reference the data beginning and end as a void* array, and | |||||
| BINARY_SIZE(Test_dat) | |||||
| to get its size in bytes. | |||||
| */ | |||||
| #ifdef ARCH_MAC | |||||
| // Use output from `xxd -i` | |||||
| #define BINARY(sym) extern unsigned char sym[]; extern unsigned int sym##_len | |||||
| #define BINARY_START(sym) ((const void*) sym) | |||||
| #define BINARY_END(sym) ((const void*) sym + sym##_len) | |||||
| #define BINARY_SIZE(sym) (sym##_len) | |||||
| #else | |||||
| #define BINARY(sym) extern char _binary_##sym##_start, _binary_##sym##_end, _binary_##sym##_size | |||||
| #define BINARY_START(sym) ((const void*) &_binary_##sym##_start) | |||||
| #define BINARY_END(sym) ((const void*) &_binary_##sym##_end) | |||||
| // The symbol "_binary_##sym##_size" doesn't seem to be valid after a plugin is dynamically loaded, so simply take the difference between the two addresses. | |||||
| #define BINARY_SIZE(sym) ((size_t) (&_binary_##sym##_end - &_binary_##sym##_start)) | |||||
| #endif | |||||
| #include "macros.hpp" | |||||
| #include "math.hpp" | #include "math.hpp" | ||||
| #include "string.hpp" | |||||
| #include "logger.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -148,32 +81,6 @@ float randomNormal(); | |||||
| DEPRECATED inline float randomf() {return randomUniform();} | DEPRECATED inline float randomf() {return randomUniform();} | ||||
| //////////////////// | |||||
| // String utilities | |||||
| // string.cpp | |||||
| //////////////////// | |||||
| /** Converts a printf format string and optional arguments into a std::string */ | |||||
| std::string stringf(const char *format, ...); | |||||
| std::string stringLowercase(std::string s); | |||||
| std::string stringUppercase(std::string s); | |||||
| /** Truncates and adds "..." to a string, not exceeding `len` characters */ | |||||
| std::string stringEllipsize(std::string s, size_t len); | |||||
| bool stringStartsWith(std::string str, std::string prefix); | |||||
| bool stringEndsWith(std::string str, std::string suffix); | |||||
| /** Extracts portions of a path */ | |||||
| std::string stringDirectory(std::string path); | |||||
| std::string stringFilename(std::string path); | |||||
| std::string stringExtension(std::string path); | |||||
| struct StringCaseInsensitiveCompare { | |||||
| bool operator()(const std::string &a, const std::string &b) const { | |||||
| return stringLowercase(a) < stringLowercase(b); | |||||
| } | |||||
| }; | |||||
| //////////////////// | //////////////////// | ||||
| // Operating-system specific utilities | // Operating-system specific utilities | ||||
| // system.cpp | // system.cpp | ||||
| @@ -196,26 +103,6 @@ void systemOpenBrowser(std::string url); | |||||
| // logger.cpp | // logger.cpp | ||||
| //////////////////// | //////////////////// | ||||
| enum LoggerLevel { | |||||
| DEBUG_LEVEL = 0, | |||||
| INFO_LEVEL, | |||||
| WARN_LEVEL, | |||||
| FATAL_LEVEL | |||||
| }; | |||||
| void loggerInit(bool devMode); | |||||
| void loggerDestroy(); | |||||
| /** Do not use this function directly. Use the macros below. */ | |||||
| void loggerLog(LoggerLevel level, const char *file, int line, const char *format, ...); | |||||
| /** Example usage: | |||||
| debug("error: %d", errno); | |||||
| will print something like | |||||
| [0.123 debug myfile.cpp:45] error: 67 | |||||
| */ | |||||
| #define debug(format, ...) loggerLog(DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define info(format, ...) loggerLog(INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define warn(format, ...) loggerLog(WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| #define fatal(format, ...) loggerLog(FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) | |||||
| //////////////////// | //////////////////// | ||||
| // Thread functions | // Thread functions | ||||
| @@ -7,7 +7,7 @@ | |||||
| #include "util/common.hpp" | #include "util/common.hpp" | ||||
| #include "events.hpp" | #include "events.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -121,13 +121,13 @@ struct MidiCcChoice : GridChoice { | |||||
| void step() override { | void step() override { | ||||
| if (module->learningId == id) { | if (module->learningId == id) { | ||||
| if (0 <= focusCc) | if (0 <= focusCc) | ||||
| text = stringf("%d", focusCc); | |||||
| text = string::stringf("%d", focusCc); | |||||
| else | else | ||||
| text = "LRN"; | text = "LRN"; | ||||
| color.a = 0.5; | color.a = 0.5; | ||||
| } | } | ||||
| else { | else { | ||||
| text = stringf("%d", module->learnedCcs[id]); | |||||
| text = string::stringf("%d", module->learnedCcs[id]); | |||||
| color.a = 1.0; | color.a = 1.0; | ||||
| if (gFocusedWidget == this) | if (gFocusedWidget == this) | ||||
| gFocusedWidget = NULL; | gFocusedWidget = NULL; | ||||
| @@ -317,7 +317,7 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { | |||||
| menu->addChild(construct<MenuLabel>()); | menu->addChild(construct<MenuLabel>()); | ||||
| for (int i = 0; i < 2; i++) { | for (int i = 0; i < 2; i++) { | ||||
| ClockItem *item = MenuItem::create<ClockItem>(stringf("CLK %d rate", i + 1)); | |||||
| ClockItem *item = MenuItem::create<ClockItem>(string::stringf("CLK %d rate", i + 1)); | |||||
| item->module = module; | item->module = module; | ||||
| item->index = i; | item->index = i; | ||||
| menu->addChild(item); | menu->addChild(item); | ||||
| @@ -168,7 +168,7 @@ struct MidiTrigChoice : GridChoice { | |||||
| }; | }; | ||||
| int oct = note / 12 - 1; | int oct = note / 12 - 1; | ||||
| int semi = note % 12; | int semi = note % 12; | ||||
| text = stringf("%s%d", noteNames[semi], oct); | |||||
| text = string::stringf("%s%d", noteNames[semi], oct); | |||||
| color.a = 1.0; | color.a = 1.0; | ||||
| if (gFocusedWidget == this) | if (gFocusedWidget == this) | ||||
| @@ -106,13 +106,13 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||||
| AudioSampleRateItem *item = new AudioSampleRateItem(); | AudioSampleRateItem *item = new AudioSampleRateItem(); | ||||
| item->audioIO = audioWidget->audioIO; | item->audioIO = audioWidget->audioIO; | ||||
| item->sampleRate = sampleRate; | item->sampleRate = sampleRate; | ||||
| item->text = stringf("%d Hz", sampleRate); | |||||
| item->text = string::stringf("%d Hz", sampleRate); | |||||
| item->rightText = CHECKMARK(item->sampleRate == audioWidget->audioIO->sampleRate); | item->rightText = CHECKMARK(item->sampleRate == audioWidget->audioIO->sampleRate); | ||||
| menu->addChild(item); | menu->addChild(item); | ||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| text = stringf("%g kHz", audioWidget->audioIO->sampleRate / 1000.f); | |||||
| text = string::stringf("%g kHz", audioWidget->audioIO->sampleRate / 1000.f); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -139,13 +139,13 @@ struct AudioBlockSizeChoice : LedDisplayChoice { | |||||
| item->audioIO = audioWidget->audioIO; | item->audioIO = audioWidget->audioIO; | ||||
| item->blockSize = blockSize; | item->blockSize = blockSize; | ||||
| float latency = (float) blockSize / audioWidget->audioIO->sampleRate * 1000.0; | float latency = (float) blockSize / audioWidget->audioIO->sampleRate * 1000.0; | ||||
| item->text = stringf("%d (%.1f ms)", blockSize, latency); | |||||
| item->text = string::stringf("%d (%.1f ms)", blockSize, latency); | |||||
| item->rightText = CHECKMARK(item->blockSize == audioWidget->audioIO->blockSize); | item->rightText = CHECKMARK(item->blockSize == audioWidget->audioIO->blockSize); | ||||
| menu->addChild(item); | menu->addChild(item); | ||||
| } | } | ||||
| } | } | ||||
| void step() override { | void step() override { | ||||
| text = stringf("%d", audioWidget->audioIO->blockSize); | |||||
| text = string::stringf("%d", audioWidget->audioIO->blockSize); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -1,5 +1,5 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -18,8 +18,8 @@ static ModelTag sTagFilter = NO_TAG; | |||||
| bool isMatch(std::string s, std::string search) { | bool isMatch(std::string s, std::string search) { | ||||
| s = stringLowercase(s); | |||||
| search = stringLowercase(search); | |||||
| s = string::lowercase(s); | |||||
| search = string::lowercase(search); | |||||
| return (s.find(search) != std::string::npos); | return (s.find(search) != std::string::npos); | ||||
| } | } | ||||
| @@ -279,7 +279,7 @@ struct ModuleBrowser : OpaqueWidget { | |||||
| SearchModuleField *searchField; | SearchModuleField *searchField; | ||||
| ScrollWidget *moduleScroll; | ScrollWidget *moduleScroll; | ||||
| BrowserList *moduleList; | BrowserList *moduleList; | ||||
| std::set<std::string, StringCaseInsensitiveCompare> availableAuthors; | |||||
| std::set<std::string, string::CaseInsensitiveCompare> availableAuthors; | |||||
| std::set<ModelTag> availableTags; | std::set<ModelTag> availableTags; | ||||
| ModuleBrowser() { | ModuleBrowser() { | ||||
| @@ -203,7 +203,7 @@ void ModuleWidget::load(std::string filename) { | |||||
| json_decref(moduleJ); | json_decref(moduleJ); | ||||
| } | } | ||||
| else { | else { | ||||
| std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||||
| std::string message = string::stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||||
| osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | ||||
| } | } | ||||
| @@ -248,7 +248,7 @@ void ModuleWidget::saveDialog() { | |||||
| if (path) { | if (path) { | ||||
| std::string pathStr = path; | std::string pathStr = path; | ||||
| free(path); | free(path); | ||||
| std::string extension = stringExtension(pathStr); | |||||
| std::string extension = string::extension(pathStr); | |||||
| if (extension.empty()) { | if (extension.empty()) { | ||||
| pathStr += ".vcvm"; | pathStr += ".vcvm"; | ||||
| } | } | ||||
| @@ -304,7 +304,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||||
| nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5)); | nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5)); | ||||
| nvgFill(vg); | nvgFill(vg); | ||||
| std::string cpuText = stringf("%.0f mS", module->cpuTime * 1000.f); | |||||
| std::string cpuText = string::stringf("%.0f mS", module->cpuTime * 1000.f); | |||||
| nvgFontFaceId(vg, gGuiFont->handle); | nvgFontFaceId(vg, gGuiFont->handle); | ||||
| nvgFontSize(vg, 12); | nvgFontSize(vg, 12); | ||||
| nvgFillColor(vg, nvgRGBf(1, 1, 1)); | nvgFillColor(vg, nvgRGBf(1, 1, 1)); | ||||
| @@ -1,5 +1,5 @@ | |||||
| #include "app.hpp" | #include "app.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -44,7 +44,7 @@ void RackScene::step() { | |||||
| // Version popup message | // Version popup message | ||||
| if (!gLatestVersion.empty()) { | 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()); | |||||
| std::string versionMessage = string::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())) { | if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, versionMessage.c_str())) { | ||||
| std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | std::thread t(systemOpenBrowser, "https://vcvrack.com/"); | ||||
| t.detach(); | t.detach(); | ||||
| @@ -116,7 +116,7 @@ void RackScene::onHoverKey(EventHoverKey &e) { | |||||
| void RackScene::onPathDrop(EventPathDrop &e) { | void RackScene::onPathDrop(EventPathDrop &e) { | ||||
| if (e.paths.size() >= 1) { | if (e.paths.size() >= 1) { | ||||
| const std::string &firstPath = e.paths.front(); | const std::string &firstPath = e.paths.front(); | ||||
| if (stringExtension(firstPath) == "vcv") { | |||||
| if (string::extension(firstPath) == "vcv") { | |||||
| gRackWidget->load(firstPath); | gRackWidget->load(firstPath); | ||||
| e.consumed = true; | e.consumed = true; | ||||
| } | } | ||||
| @@ -75,7 +75,7 @@ void RackWidget::loadDialog() { | |||||
| systemCreateDirectory(dir); | systemCreateDirectory(dir); | ||||
| } | } | ||||
| else { | else { | ||||
| dir = stringDirectory(lastPath); | |||||
| dir = string::directory(lastPath); | |||||
| } | } | ||||
| osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | ||||
| char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); | ||||
| @@ -104,8 +104,8 @@ void RackWidget::saveAsDialog() { | |||||
| systemCreateDirectory(dir); | systemCreateDirectory(dir); | ||||
| } | } | ||||
| else { | else { | ||||
| dir = stringDirectory(lastPath); | |||||
| filename = stringFilename(lastPath); | |||||
| dir = string::directory(lastPath); | |||||
| filename = string::filename(lastPath); | |||||
| } | } | ||||
| osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str()); | ||||
| char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); | char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); | ||||
| @@ -113,7 +113,7 @@ void RackWidget::saveAsDialog() { | |||||
| if (path) { | if (path) { | ||||
| std::string pathStr = path; | std::string pathStr = path; | ||||
| free(path); | free(path); | ||||
| std::string extension = stringExtension(pathStr); | |||||
| std::string extension = string::extension(pathStr); | |||||
| if (extension.empty()) { | if (extension.empty()) { | ||||
| pathStr += ".vcv"; | pathStr += ".vcv"; | ||||
| } | } | ||||
| @@ -155,7 +155,7 @@ void RackWidget::load(std::string filename) { | |||||
| json_decref(rootJ); | json_decref(rootJ); | ||||
| } | } | ||||
| else { | else { | ||||
| std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||||
| std::string message = string::stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||||
| osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | ||||
| } | } | ||||
| @@ -259,7 +259,7 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| // Detect old patches with ModuleWidget::params/inputs/outputs indices. | // Detect old patches with ModuleWidget::params/inputs/outputs indices. | ||||
| // (We now use Module::params/inputs/outputs indices.) | // (We now use Module::params/inputs/outputs indices.) | ||||
| int legacy = 0; | int legacy = 0; | ||||
| if (stringStartsWith(version, "0.3.") || stringStartsWith(version, "0.4.") || stringStartsWith(version, "0.5.") || version == "" || version == "dev") { | |||||
| if (string::startsWith(version, "0.3.") || string::startsWith(version, "0.4.") || string::startsWith(version, "0.5.") || version == "" || version == "dev") { | |||||
| legacy = 1; | legacy = 1; | ||||
| } | } | ||||
| if (legacy) { | if (legacy) { | ||||
| @@ -300,7 +300,7 @@ void RackWidget::fromJson(json_t *rootJ) { | |||||
| json_t *modelSlugJ = json_object_get(moduleJ, "model"); | json_t *modelSlugJ = json_object_get(moduleJ, "model"); | ||||
| std::string pluginSlug = json_string_value(pluginSlugJ); | std::string pluginSlug = json_string_value(pluginSlugJ); | ||||
| std::string modelSlug = json_string_value(modelSlugJ); | std::string modelSlug = json_string_value(modelSlugJ); | ||||
| message += stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); | |||||
| message += string::stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -132,7 +132,7 @@ struct SampleRateButton : TooltipIconButton { | |||||
| std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | ||||
| for (float sampleRate : sampleRates) { | for (float sampleRate : sampleRates) { | ||||
| SampleRateItem *item = new SampleRateItem(); | SampleRateItem *item = new SampleRateItem(); | ||||
| item->text = stringf("%.0f Hz", sampleRate); | |||||
| item->text = string::stringf("%.0f Hz", sampleRate); | |||||
| item->rightText = CHECKMARK(engineGetSampleRate() == sampleRate); | item->rightText = CHECKMARK(engineGetSampleRate() == sampleRate); | ||||
| item->sampleRate = sampleRate; | item->sampleRate = sampleRate; | ||||
| menu->addChild(item); | menu->addChild(item); | ||||
| @@ -18,8 +18,9 @@ std::vector<int> AudioIO::getDrivers() { | |||||
| std::vector<RtAudio::Api> apis; | std::vector<RtAudio::Api> apis; | ||||
| RtAudio::getCompiledApi(apis); | RtAudio::getCompiledApi(apis); | ||||
| std::vector<int> drivers; | std::vector<int> drivers; | ||||
| for (RtAudio::Api api : apis) | |||||
| for (RtAudio::Api api : apis) { | |||||
| drivers.push_back((int) api); | drivers.push_back((int) api); | ||||
| } | |||||
| // Add fake Bridge driver | // Add fake Bridge driver | ||||
| drivers.push_back(BRIDGE_DRIVER); | drivers.push_back(BRIDGE_DRIVER); | ||||
| return drivers; | return drivers; | ||||
| @@ -121,7 +122,7 @@ std::string AudioIO::getDeviceName(int device) { | |||||
| return deviceInfo.name; | return deviceInfo.name; | ||||
| } | } | ||||
| else if (driver == BRIDGE_DRIVER) { | else if (driver == BRIDGE_DRIVER) { | ||||
| return stringf("%d", device + 1); | |||||
| return string::stringf("%d", device + 1); | |||||
| } | } | ||||
| return ""; | return ""; | ||||
| } | } | ||||
| @@ -133,19 +134,19 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { | |||||
| if (rtAudio) { | if (rtAudio) { | ||||
| RtAudio::DeviceInfo deviceInfo; | RtAudio::DeviceInfo deviceInfo; | ||||
| if (getDeviceInfo(device, &deviceInfo)) { | if (getDeviceInfo(device, &deviceInfo)) { | ||||
| std::string deviceDetail = stringf("%s (", deviceInfo.name.c_str()); | |||||
| std::string deviceDetail = string::stringf("%s (", deviceInfo.name.c_str()); | |||||
| if (offset < (int) deviceInfo.inputChannels) | if (offset < (int) deviceInfo.inputChannels) | ||||
| deviceDetail += stringf("%d-%d in", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.inputChannels)); | |||||
| deviceDetail += string::stringf("%d-%d in", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.inputChannels)); | |||||
| if (offset < (int) deviceInfo.inputChannels && offset < (int) deviceInfo.outputChannels) | if (offset < (int) deviceInfo.inputChannels && offset < (int) deviceInfo.outputChannels) | ||||
| deviceDetail += ", "; | deviceDetail += ", "; | ||||
| if (offset < (int) deviceInfo.outputChannels) | if (offset < (int) deviceInfo.outputChannels) | ||||
| deviceDetail += stringf("%d-%d out", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.outputChannels)); | |||||
| deviceDetail += string::stringf("%d-%d out", offset + 1, std::min(offset + maxChannels, (int) deviceInfo.outputChannels)); | |||||
| deviceDetail += ")"; | deviceDetail += ")"; | ||||
| return deviceDetail; | return deviceDetail; | ||||
| } | } | ||||
| } | } | ||||
| else if (driver == BRIDGE_DRIVER) { | else if (driver == BRIDGE_DRIVER) { | ||||
| return stringf("Port %d", device + 1); | |||||
| return string::stringf("Port %d", device + 1); | |||||
| } | } | ||||
| return ""; | return ""; | ||||
| } | } | ||||
| @@ -384,7 +384,7 @@ std::vector<int> BridgeMidiDriver::getInputDeviceIds() { | |||||
| std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | ||||
| if (deviceId < 0) | if (deviceId < 0) | ||||
| return ""; | return ""; | ||||
| return stringf("Port %d", deviceId + 1); | |||||
| return string::stringf("Port %d", deviceId + 1); | |||||
| } | } | ||||
| MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | ||||
| @@ -1,5 +1,5 @@ | |||||
| #include "gamepad.hpp" | |||||
| #include <GLFW/glfw3.h> | #include <GLFW/glfw3.h> | ||||
| #include "gamepad.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -22,7 +22,7 @@ void GamepadInputDevice::step() { | |||||
| ccs.resize(numAxes); | ccs.resize(numAxes); | ||||
| for (int i = 0; i < numAxes; i++) { | for (int i = 0; i < numAxes; i++) { | ||||
| // Allow CC value to go negative, but math::clamp at -127 instead of -128 for symmetry | // Allow CC value to go negative, but math::clamp at -127 instead of -128 for symmetry | ||||
| int8_t cc = math::clamp((int) roundf(axes[i] * 127), -127, 127); | |||||
| int8_t cc = math::clamp((int) std::round(axes[i] * 127), -127, 127); | |||||
| if (cc != ccs[i]) { | if (cc != ccs[i]) { | ||||
| ccs[i] = cc; | ccs[i] = cc; | ||||
| @@ -77,7 +77,7 @@ std::string GamepadDriver::getInputDeviceName(int deviceId) { | |||||
| if (name) { | if (name) { | ||||
| return name; | return name; | ||||
| } | } | ||||
| return stringf("Gamepad %d (unavailable)", deviceId + 1); | |||||
| return string::stringf("Gamepad %d (unavailable)", deviceId + 1); | |||||
| } | } | ||||
| MidiInputDevice *GamepadDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | MidiInputDevice *GamepadDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) { | ||||
| @@ -0,0 +1,68 @@ | |||||
| #include <cstdarg> | |||||
| #include <chrono> | |||||
| #include "logger.hpp" | |||||
| #include "asset.hpp" | |||||
| namespace rack { | |||||
| namespace logger { | |||||
| static FILE *outputFile = NULL; | |||||
| static std::chrono::high_resolution_clock::time_point startTime; | |||||
| void init(bool devMode) { | |||||
| startTime = std::chrono::high_resolution_clock::now(); | |||||
| if (devMode) { | |||||
| outputFile = stderr; | |||||
| } | |||||
| else { | |||||
| std::string logFilename = assetLocal("log.txt"); | |||||
| outputFile = fopen(logFilename.c_str(), "w"); | |||||
| } | |||||
| } | |||||
| void destroy() { | |||||
| if (outputFile != stderr) { | |||||
| fclose(outputFile); | |||||
| } | |||||
| } | |||||
| static const char* const levelLabels[] = { | |||||
| "debug", | |||||
| "info", | |||||
| "warn", | |||||
| "fatal" | |||||
| }; | |||||
| static const int levelColors[] = { | |||||
| 35, | |||||
| 34, | |||||
| 33, | |||||
| 31 | |||||
| }; | |||||
| static void logVa(Level level, const char *filename, int line, const char *format, va_list args) { | |||||
| auto nowTime = std::chrono::high_resolution_clock::now(); | |||||
| int duration = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - startTime).count(); | |||||
| if (outputFile == stderr) | |||||
| fprintf(outputFile, "\x1B[%dm", levelColors[level]); | |||||
| fprintf(outputFile, "[%.03f %s %s:%d] ", duration / 1000.0, levelLabels[level], filename, line); | |||||
| if (outputFile == stderr) | |||||
| fprintf(outputFile, "\x1B[0m"); | |||||
| vfprintf(outputFile, format, args); | |||||
| fprintf(outputFile, "\n"); | |||||
| fflush(outputFile); | |||||
| } | |||||
| void log(Level level, const char *filename, int line, const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| logVa(level, filename, line, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| } // namespace logger | |||||
| } // namespace rack | |||||
| @@ -10,7 +10,7 @@ | |||||
| #include "rtmidi.hpp" | #include "rtmidi.hpp" | ||||
| #include "keyboard.hpp" | #include "keyboard.hpp" | ||||
| #include "gamepad.hpp" | #include "gamepad.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| #include "osdialog.h" | #include "osdialog.h" | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| @@ -61,7 +61,7 @@ int main(int argc, char* argv[]) { | |||||
| // Initialize environment | // Initialize environment | ||||
| randomInit(); | randomInit(); | ||||
| assetInit(devMode); | assetInit(devMode); | ||||
| loggerInit(devMode); | |||||
| logger::init(devMode); | |||||
| // Log environment | // Log environment | ||||
| info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str()); | info("%s %s", gApplicationName.c_str(), gApplicationVersion.c_str()); | ||||
| @@ -116,7 +116,7 @@ int main(int argc, char* argv[]) { | |||||
| engineDestroy(); | engineDestroy(); | ||||
| midiDestroy(); | midiDestroy(); | ||||
| pluginDestroy(); | pluginDestroy(); | ||||
| loggerDestroy(); | |||||
| logger::destroy(); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -79,7 +79,7 @@ std::string MidiIO::getChannelName(int channel) { | |||||
| if (channel == -1) | if (channel == -1) | ||||
| return "All channels"; | return "All channels"; | ||||
| else | else | ||||
| return stringf("Channel %d", channel + 1); | |||||
| return string::stringf("Channel %d", channel + 1); | |||||
| } | } | ||||
| json_t *MidiIO::toJson() { | json_t *MidiIO::toJson() { | ||||
| @@ -216,7 +216,7 @@ static void loadPlugins(std::string path) { | |||||
| if (!systemIsDirectory(pluginPath)) | if (!systemIsDirectory(pluginPath)) | ||||
| continue; | continue; | ||||
| if (!loadPlugin(pluginPath)) { | if (!loadPlugin(pluginPath)) { | ||||
| message += stringf("Could not load plugin %s\n", pluginPath.c_str()); | |||||
| message += string::stringf("Could not load plugin %s\n", pluginPath.c_str()); | |||||
| } | } | ||||
| } | } | ||||
| if (!message.empty()) { | if (!message.empty()) { | ||||
| @@ -298,13 +298,13 @@ static void extractPackages(std::string path) { | |||||
| std::string message; | std::string message; | ||||
| for (std::string packagePath : systemListEntries(path)) { | for (std::string packagePath : systemListEntries(path)) { | ||||
| if (stringExtension(packagePath) != "zip") | |||||
| if (string::extension(packagePath) != "zip") | |||||
| continue; | continue; | ||||
| info("Extracting package %s", packagePath.c_str()); | info("Extracting package %s", packagePath.c_str()); | ||||
| // Extract package | // Extract package | ||||
| if (extractZip(packagePath.c_str(), path.c_str())) { | if (extractZip(packagePath.c_str(), path.c_str())) { | ||||
| warn("Package %s failed to extract", packagePath.c_str()); | warn("Package %s failed to extract", packagePath.c_str()); | ||||
| message += stringf("Could not extract package %s\n", packagePath.c_str()); | |||||
| message += string::stringf("Could not extract package %s\n", packagePath.c_str()); | |||||
| continue; | continue; | ||||
| } | } | ||||
| // Remove package | // Remove package | ||||
| @@ -1,6 +1,6 @@ | |||||
| #include "ui.hpp" | #include "ui.hpp" | ||||
| #include "window.hpp" | #include "window.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -1,100 +0,0 @@ | |||||
| #include "util/common.hpp" | |||||
| #include "asset.hpp" | |||||
| #include <stdarg.h> | |||||
| namespace rack { | |||||
| static FILE *logFile = NULL; | |||||
| static std::chrono::high_resolution_clock::time_point startTime; | |||||
| void loggerInit(bool devMode) { | |||||
| startTime = std::chrono::high_resolution_clock::now(); | |||||
| if (devMode) { | |||||
| logFile = stderr; | |||||
| } | |||||
| else { | |||||
| std::string logFilename = assetLocal("log.txt"); | |||||
| logFile = fopen(logFilename.c_str(), "w"); | |||||
| } | |||||
| } | |||||
| void loggerDestroy() { | |||||
| if (logFile != stderr) { | |||||
| fclose(logFile); | |||||
| } | |||||
| } | |||||
| static const char* const loggerText[] = { | |||||
| "debug", | |||||
| "info", | |||||
| "warn", | |||||
| "fatal" | |||||
| }; | |||||
| static const int loggerColor[] = { | |||||
| 35, | |||||
| 34, | |||||
| 33, | |||||
| 31 | |||||
| }; | |||||
| static void loggerLogVa(LoggerLevel level, const char *file, int line, const char *format, va_list args) { | |||||
| auto nowTime = std::chrono::high_resolution_clock::now(); | |||||
| int duration = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - startTime).count(); | |||||
| if (logFile == stderr) | |||||
| fprintf(logFile, "\x1B[%dm", loggerColor[level]); | |||||
| fprintf(logFile, "[%.03f %s %s:%d] ", duration / 1000.0, loggerText[level], file, line); | |||||
| if (logFile == stderr) | |||||
| fprintf(logFile, "\x1B[0m"); | |||||
| vfprintf(logFile, format, args); | |||||
| fprintf(logFile, "\n"); | |||||
| fflush(logFile); | |||||
| } | |||||
| void loggerLog(LoggerLevel level, const char *file, int line, const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| loggerLogVa(level, file, line, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| /** Deprecated. Included for ABI compatibility */ | |||||
| #undef debug | |||||
| #undef info | |||||
| #undef warn | |||||
| #undef fatal | |||||
| void debug(const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| loggerLogVa(DEBUG_LEVEL, "", 0, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| void info(const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| loggerLogVa(INFO_LEVEL, "", 0, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| void warn(const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| loggerLogVa(WARN_LEVEL, "", 0, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| void fatal(const char *format, ...) { | |||||
| va_list args; | |||||
| va_start(args, format); | |||||
| loggerLogVa(FATAL_LEVEL, "", 0, format, args); | |||||
| va_end(args); | |||||
| } | |||||
| } // namespace rack | |||||
| @@ -27,7 +27,7 @@ void QuantityWidget::setDefaultValue(float defaultValue) { | |||||
| std::string QuantityWidget::getText() { | std::string QuantityWidget::getText() { | ||||
| std::string text = label; | std::string text = label; | ||||
| text += ": "; | text += ": "; | ||||
| text += stringf("%.*f", precision, value); | |||||
| text += string::stringf("%.*f", precision, value); | |||||
| text += unit; | text += unit; | ||||
| return text; | return text; | ||||
| } | } | ||||
| @@ -3,7 +3,7 @@ | |||||
| #include "asset.hpp" | #include "asset.hpp" | ||||
| #include "gamepad.hpp" | #include "gamepad.hpp" | ||||
| #include "keyboard.hpp" | #include "keyboard.hpp" | ||||
| #include "util/color.hpp" | |||||
| #include "color.hpp" | |||||
| #include <map> | #include <map> | ||||
| #include <queue> | #include <queue> | ||||
| @@ -454,7 +454,7 @@ void windowRun() { | |||||
| windowTitle += gApplicationVersion; | windowTitle += gApplicationVersion; | ||||
| if (!gRackWidget->lastPath.empty()) { | if (!gRackWidget->lastPath.empty()) { | ||||
| windowTitle += " - "; | windowTitle += " - "; | ||||
| windowTitle += stringFilename(gRackWidget->lastPath); | |||||
| windowTitle += string::filename(gRackWidget->lastPath); | |||||
| } | } | ||||
| if (windowTitle != lastWindowTitle) { | if (windowTitle != lastWindowTitle) { | ||||
| glfwSetWindowTitle(gWindow, windowTitle.c_str()); | glfwSetWindowTitle(gWindow, windowTitle.c_str()); | ||||