diff --git a/include/app/SvgScrew.hpp b/include/app/SvgScrew.hpp index a0810129..bc577649 100644 --- a/include/app/SvgScrew.hpp +++ b/include/app/SvgScrew.hpp @@ -1,5 +1,6 @@ #pragma once #include "common.hpp" +#include "widget/Widget.hpp" #include "widget/FramebufferWidget.hpp" #include "widget/SvgWidget.hpp" @@ -9,10 +10,12 @@ namespace app { /** If you don't add these to your ModuleWidget, they will fall out of the rack... */ -struct SvgScrew : widget::FramebufferWidget { +struct SvgScrew : widget::Widget { + widget::FramebufferWidget *fb; widget::SvgWidget *sw; SvgScrew(); + void setSvg(std::shared_ptr svg); }; diff --git a/include/common.hpp b/include/common.hpp index 757f35e4..3c1aa373 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -21,6 +21,7 @@ namespace rack { /** Deprecation notice for functions E.g. + DEPRECATED void foo(); */ #if defined(__GNUC__) || defined(__clang__) @@ -33,9 +34,12 @@ E.g. /** Concatenates two literals or two macros Example: + #define COUNT 42 CONCAT(myVariable, COUNT) + expands to + myVariable42 */ #define CONCAT_LITERAL(x, y) x ## y @@ -44,10 +48,14 @@ expands to /** 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 @@ -60,26 +68,35 @@ and of course the C++ lexer/parser then concatenates the string literals. /** 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. +`BAR + 0` to `BAR + 13` is reserved. `BAZ` has a value of 14. */ #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 /** 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. */ #if defined ARCH_MAC @@ -99,14 +116,15 @@ to get its size in bytes. /** C#-style property constructor Example: + Foo *foo = construct(&Foo::greeting, "Hello world", &Foo::legs, 2); */ -template +template T *construct() { return new T; } -template +template T *construct(F f, V v, Args... args) { T *o = construct(args...); o->*f = v; @@ -115,21 +133,21 @@ T *construct(F f, V v, Args... args) { /** Defers code until the scope is destructed From http://www.gingerbill.org/article/defer-in-cpp.html - Example: + file = fopen(...); DEFER({ fclose(file); }); */ -template +template struct DeferWrapper { F f; DeferWrapper(F f) : f(f) {} ~DeferWrapper() { f(); } }; -template +template DeferWrapper deferWrapper(F f) { return DeferWrapper(f); } @@ -137,7 +155,7 @@ DeferWrapper deferWrapper(F f) { #define DEFER(code) auto CONCAT(_defer_, __COUNTER__) = rack::deferWrapper([&]() code) -/** Reports a string to the user */ +/** An exception meant to be shown to the user */ struct UserException : std::runtime_error { UserException(const std::string &msg) : std::runtime_error(msg) {} }; diff --git a/include/componentlibrary.hpp b/include/componentlibrary.hpp index 3c712a2d..e7166a8b 100644 --- a/include/componentlibrary.hpp +++ b/include/componentlibrary.hpp @@ -300,9 +300,12 @@ struct SynthTechAlco : app::SvgKnob { minAngle = -0.82*M_PI; maxAngle = 0.82*M_PI; setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); + // Add cap + widget::FramebufferWidget *capFb = new widget::FramebufferWidget; widget::SvgWidget *cap = new widget::SvgWidget; cap->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco_cap.svg"))); - addChild(cap); + capFb->addChild(cap); + addChild(capFb); } }; @@ -460,15 +463,6 @@ struct RedGreenBlueLight : GrayModuleLightWidget { } }; -struct RGBLight : app::ModuleLightWidget { - RGBLight() { - addBaseColor(nvgRGBf(1, 0, 0)); - addBaseColor(nvgRGBf(0, 1, 0)); - addBaseColor(nvgRGBf(0, 0, 1)); - } -}; - - /** Based on the size of 5mm LEDs */ template struct LargeLight : BASE { @@ -501,7 +495,7 @@ struct TinyLight : BASE { } }; -/** A light to displayed over PB61303. Must add a color by subclassing or templating. */ +/** A light for displaying on top of PB61303. Must add a color by subclassing or templating. */ template struct LEDBezelLight : BASE { LEDBezelLight() { @@ -608,15 +602,13 @@ struct PB61303 : app::SvgSwitch { struct ScrewSilver : app::SvgScrew { ScrewSilver() { - sw->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/ScrewSilver.svg"))); - box.size = sw->box.size; + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/ScrewSilver.svg"))); } }; struct ScrewBlack : app::SvgScrew { ScrewBlack() { - sw->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/ScrewBlack.svg"))); - box.size = sw->box.size; + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/ScrewBlack.svg"))); } }; diff --git a/include/event.hpp b/include/event.hpp index 6ce81894..f8953dcb 100644 --- a/include/event.hpp +++ b/include/event.hpp @@ -5,6 +5,28 @@ #include + +/** Remaps Ctrl to Cmd on Mac +Use this instead of GLFW_MOD_CONTROL, since Cmd should be used on Mac in place of Ctrl on Linux/Windows. +*/ +#if defined ARCH_MAC + #define RACK_MOD_CTRL GLFW_MOD_SUPER + #define RACK_MOD_CTRL_NAME "Cmd" +#else + #define RACK_MOD_CTRL GLFW_MOD_CONTROL + #define RACK_MOD_CTRL_NAME "Ctrl" +#endif +#define RACK_MOD_SHIFT_NAME "Shift" +#define RACK_MOD_ALT_NAME "Alt" + +/** Filters actual mod keys from the mod flags. +Use this if you don't care about GLFW_MOD_CAPS_LOCK and GLFW_MOD_NUM_LOCK. +Example usage: + if ((e.mod & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) ... +*/ +#define RACK_MOD_MASK (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) + + namespace rack { diff --git a/include/helpers.hpp b/include/helpers.hpp index 057695d5..574bff47 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -52,8 +52,8 @@ TWidget *createWidget(math::Vec pos) { template TWidget *createWidgetCentered(math::Vec pos) { - TWidget *o = new TWidget; - o->box.pos = pos.minus(o->box.size.div(2));; + TWidget *o = createWidget(pos); + o->box.pos = o->box.pos.minus(o->box.size.div(2)); return o; } diff --git a/include/logger.hpp b/include/logger.hpp index f77ff97f..e35cc12f 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -2,14 +2,17 @@ /** 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__) +#define DEBUG(format, ...) logger::log(rack::logger::DEBUG_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define INFO(format, ...) logger::log(rack::logger::INFO_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define WARN(format, ...) logger::log(rack::logger::WARN_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) +#define FATAL(format, ...) logger::log(rack::logger::FATAL_LEVEL, __FILE__, __LINE__, format, ##__VA_ARGS__) namespace rack { @@ -29,8 +32,8 @@ enum Level { void init(); void destroy(); -/** Do not use this function directly. Use the macros below. -Thread-safe. +/** Do not use this function directly. Use the macros above. +Thread-safe, meaning messages cannot overlap each other in the log. */ void log(Level level, const char *filename, int line, const char *format, ...); diff --git a/include/rack.hpp b/include/rack.hpp index fa2cbf82..d681d596 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -101,6 +101,7 @@ namespace rack { /** Define this macro before including this header to prevent common namespaces from being included in the main `rack::` namespace. */ #ifndef RACK_FLATTEN_NAMESPACES // Import some namespaces for convenience +using namespace logger; using namespace math; using namespace widget; using namespace ui; diff --git a/include/string.hpp b/include/string.hpp index 96cd07e6..a75c9d14 100644 --- a/include/string.hpp +++ b/include/string.hpp @@ -13,14 +13,15 @@ namespace string { /** Converts a UTF-16/32 string (depending on the size of wchar_t) to a UTF-8 string. */ std::string fromWstring(const std::wstring &s); std::wstring toWstring(const std::string &s); -/** Converts a `printf()` format string and optional arguments into a std::string -Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s. +/** Converts a `printf()` format string and optional arguments into a std::string. +Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s, otherwise you might get binary garbage. */ std::string f(const char *format, ...); /** Replaces all characters to lowercase letters */ std::string lowercase(const std::string &s); /** Replaces all characters to uppercase letters */ std::string uppercase(const std::string &s); +/** Removes whitespace from beginning and end of string. */ std::string trim(const std::string &s); /** Truncates and adds "..." to a string, not exceeding `len` characters */ std::string ellipsize(const std::string &s, size_t len); diff --git a/include/system.hpp b/include/system.hpp index ec9dfded..a685b981 100644 --- a/include/system.hpp +++ b/include/system.hpp @@ -12,11 +12,13 @@ namespace system { /** Returns a list of all entries (directories, files, symbols) in a directory. */ -std::list listEntries(const std::string &path); +std::list getEntries(const std::string &path); /** Returns whether the given path is a file. */ bool isFile(const std::string &path); /** Returns whether the given path is a directory. */ bool isDirectory(const std::string &path); +/** Moves a file. */ +void moveFile(const std::string &srcPath, const std::string &destPath); /** Copies a file. */ void copyFile(const std::string &srcPath, const std::string &destPath); /** Creates a directory. @@ -36,12 +38,12 @@ Shell injection is possible, so make sure the URL is trusted or hard coded. May block, so open in a new thread. */ void openBrowser(const std::string &url); -/** Opens Explorer, Finder, etc at the folder location. */ +/** Opens Windows Explorer, Finder, etc at the folder location. */ void openFolder(const std::string &path); /** Runs an executable without blocking. The launched process will continue running if the current process is closed. */ -void runProcessAsync(const std::string &path); +void runProcessDetached(const std::string &path); std::string getOperatingSystemInfo(); diff --git a/include/window.hpp b/include/window.hpp index 4a58d70c..8ada93ae 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -14,27 +14,6 @@ #include -/** Remaps Ctrl to Cmd on Mac -Use this instead of GLFW_MOD_CONTROL, since Cmd should be used on Mac in place of Ctrl on Linux/Windows. -*/ -#if defined ARCH_MAC - #define RACK_MOD_CTRL GLFW_MOD_SUPER - #define RACK_MOD_CTRL_NAME "Cmd" -#else - #define RACK_MOD_CTRL GLFW_MOD_CONTROL - #define RACK_MOD_CTRL_NAME "Ctrl" -#endif -#define RACK_MOD_SHIFT_NAME "Shift" -#define RACK_MOD_ALT_NAME "Alt" - -/** Filters actual mod keys from the mod flags. -Use this if you don't care about GLFW_MOD_CAPS_LOCK and GLFW_MOD_NUM_LOCK. -Example usage: - if ((e.mod & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) ... -*/ -#define RACK_MOD_MASK (GLFW_MOD_SHIFT | GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) - - namespace rack { diff --git a/src/app/SvgScrew.cpp b/src/app/SvgScrew.cpp index 5b8bb335..6636e543 100644 --- a/src/app/SvgScrew.cpp +++ b/src/app/SvgScrew.cpp @@ -6,8 +6,18 @@ namespace app { SvgScrew::SvgScrew() { + fb = new widget::FramebufferWidget; + addChild(fb); + sw = new widget::SvgWidget; - addChild(sw); + fb->addChild(sw); +} + + +void SvgScrew::setSvg(std::shared_ptr svg) { + sw->setSvg(svg); + fb->box.size = sw->box.size; + box.size = sw->box.size; } diff --git a/src/patch.cpp b/src/patch.cpp index 69840655..7a20524f 100644 --- a/src/patch.cpp +++ b/src/patch.cpp @@ -91,8 +91,7 @@ void PatchManager::save(std::string path) { json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); std::fclose(file); - std::remove(path.c_str()); - std::rename(tmpPath.c_str(), path.c_str()); + system::moveFile(tmpPath, path); } void PatchManager::saveDialog() { diff --git a/src/plugin.cpp b/src/plugin.cpp index 1e6d6e4f..a4295693 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -149,7 +149,7 @@ static bool loadPlugin(std::string path) { // Search for presets for (Model *model : plugin->models) { std::string presetDir = asset::plugin(plugin, "presets/" + model->slug); - for (const std::string &presetPath : system::listEntries(presetDir)) { + for (const std::string &presetPath : system::getEntries(presetDir)) { model->presetPaths.push_back(presetPath); } } @@ -190,7 +190,7 @@ static bool syncUpdate(const Update &update) { static void loadPlugins(std::string path) { std::string message; - for (std::string pluginPath : system::listEntries(path)) { + for (std::string pluginPath : system::getEntries(path)) { if (!system::isDirectory(pluginPath)) continue; if (!loadPlugin(pluginPath)) { @@ -271,7 +271,7 @@ static int extractZip(const char *filename, const char *path) { static void extractPackages(const std::string &path) { std::string message; - for (std::string packagePath : system::listEntries(path)) { + for (std::string packagePath : system::getEntries(path)) { if (string::filenameExtension(packagePath) != "zip") continue; INFO("Extracting package %s", packagePath.c_str()); diff --git a/src/system.cpp b/src/system.cpp index 3c7d8cbf..c7cb3f94 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -25,7 +25,7 @@ namespace rack { namespace system { -std::list listEntries(const std::string &path) { +std::list getEntries(const std::string &path) { std::list filenames; DIR *dir = opendir(path.c_str()); if (dir) { @@ -56,14 +56,23 @@ bool isDirectory(const std::string &path) { return S_ISDIR(statbuf.st_mode); } +void moveFile(const std::string &srcPath, const std::string &destPath) { + std::remove(destPath.c_str()); + // Whether this overwrites existing files is implementation-defined. + // i.e. Mingw64 fails to overwrite. + // This is why we remove the file above. + std::rename(srcPath.c_str(), destPath.c_str()); +} + void copyFile(const std::string &srcPath, const std::string &destPath) { - // Open files + // Open source FILE *source = fopen(srcPath.c_str(), "rb"); if (!source) return; DEFER({ fclose(source); }); + // Open destination FILE *dest = fopen(destPath.c_str(), "wb"); if (!dest) return; @@ -93,7 +102,6 @@ void createDirectory(const std::string &path) { } int getLogicalCoreCount() { - // TODO Return the physical cores, not logical cores. return std::thread::hardware_concurrency(); } @@ -199,7 +207,7 @@ void openFolder(const std::string &path) { } -void runProcessAsync(const std::string &path) { +void runProcessDetached(const std::string &path) { #if defined ARCH_WIN STARTUPINFOW startupInfo; PROCESS_INFORMATION processInfo; @@ -212,6 +220,9 @@ void runProcessAsync(const std::string &path) { CreateProcessW(pathW.c_str(), NULL, NULL, NULL, false, 0, NULL, NULL, &startupInfo, &processInfo); +#else + // Not implemented on Linux or Mac + assert(0); #endif } @@ -226,6 +237,7 @@ std::string getOperatingSystemInfo() { ZeroMemory(&info, sizeof(info)); info.dwOSVersionInfoSize = sizeof(info); GetVersionExW(&info); + // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers. return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion); #endif }