@@ -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> svg); | |||
}; | |||
@@ -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>(&Foo::greeting, "Hello world", &Foo::legs, 2); | |||
*/ | |||
template<typename T> | |||
template <typename T> | |||
T *construct() { | |||
return new T; | |||
} | |||
template<typename T, typename F, typename V, typename... Args> | |||
template <typename T, typename F, typename V, typename... Args> | |||
T *construct(F f, V v, Args... args) { | |||
T *o = construct<T>(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<typename F> | |||
template <typename F> | |||
struct DeferWrapper { | |||
F f; | |||
DeferWrapper(F f) : f(f) {} | |||
~DeferWrapper() { f(); } | |||
}; | |||
template<typename F> | |||
template <typename F> | |||
DeferWrapper<F> deferWrapper(F f) { | |||
return DeferWrapper<F>(f); | |||
} | |||
@@ -137,7 +155,7 @@ DeferWrapper<F> 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) {} | |||
}; | |||
@@ -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 <typename BASE> | |||
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 <typename BASE> | |||
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"))); | |||
} | |||
}; | |||
@@ -5,6 +5,28 @@ | |||
#include <set> | |||
/** 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 { | |||
@@ -52,8 +52,8 @@ TWidget *createWidget(math::Vec pos) { | |||
template <class TWidget> | |||
TWidget *createWidgetCentered(math::Vec pos) { | |||
TWidget *o = new TWidget; | |||
o->box.pos = pos.minus(o->box.size.div(2));; | |||
TWidget *o = createWidget<TWidget>(pos); | |||
o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
return o; | |||
} | |||
@@ -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, ...); | |||
@@ -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; | |||
@@ -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); | |||
@@ -12,11 +12,13 @@ namespace system { | |||
/** Returns a list of all entries (directories, files, symbols) in a directory. */ | |||
std::list<std::string> listEntries(const std::string &path); | |||
std::list<std::string> 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(); | |||
@@ -14,27 +14,6 @@ | |||
#include <nanosvg.h> | |||
/** 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 { | |||
@@ -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> svg) { | |||
sw->setSvg(svg); | |||
fb->box.size = sw->box.size; | |||
box.size = sw->box.size; | |||
} | |||
@@ -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() { | |||
@@ -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()); | |||
@@ -25,7 +25,7 @@ namespace rack { | |||
namespace system { | |||
std::list<std::string> listEntries(const std::string &path) { | |||
std::list<std::string> getEntries(const std::string &path) { | |||
std::list<std::string> 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 | |||
} | |||