@@ -1,5 +1,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "widget/Widget.hpp" | |||||
#include "widget/FramebufferWidget.hpp" | #include "widget/FramebufferWidget.hpp" | ||||
#include "widget/SvgWidget.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... */ | /** 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; | widget::SvgWidget *sw; | ||||
SvgScrew(); | SvgScrew(); | ||||
void setSvg(std::shared_ptr<Svg> svg); | |||||
}; | }; | ||||
@@ -21,6 +21,7 @@ namespace rack { | |||||
/** Deprecation notice for functions | /** Deprecation notice for functions | ||||
E.g. | E.g. | ||||
DEPRECATED void foo(); | DEPRECATED void foo(); | ||||
*/ | */ | ||||
#if defined(__GNUC__) || defined(__clang__) | #if defined(__GNUC__) || defined(__clang__) | ||||
@@ -33,9 +34,12 @@ E.g. | |||||
/** Concatenates two literals or two macros | /** Concatenates two literals or two macros | ||||
Example: | Example: | ||||
#define COUNT 42 | #define COUNT 42 | ||||
CONCAT(myVariable, COUNT) | CONCAT(myVariable, COUNT) | ||||
expands to | expands to | ||||
myVariable42 | myVariable42 | ||||
*/ | */ | ||||
#define CONCAT_LITERAL(x, y) x ## y | #define CONCAT_LITERAL(x, y) x ## y | ||||
@@ -44,10 +48,14 @@ expands to | |||||
/** Surrounds raw text with quotes | /** Surrounds raw text with quotes | ||||
Example: | Example: | ||||
#define NAME "world" | #define NAME "world" | ||||
printf("Hello " TOSTRING(NAME)) | printf("Hello " TOSTRING(NAME)) | ||||
expands to | expands to | ||||
printf("Hello " "world") | printf("Hello " "world") | ||||
and of course the C++ lexer/parser then concatenates the string literals. | and of course the C++ lexer/parser then concatenates the string literals. | ||||
*/ | */ | ||||
#define TOSTRING_LITERAL(x) #x | #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`. | /** Reserve space for `count` enums starting with `name`. | ||||
Example: | Example: | ||||
enum Foo { | enum Foo { | ||||
ENUMS(BAR, 14), | ENUMS(BAR, 14), | ||||
BAZ | 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 | #define ENUMS(name, count) name, name ## _LAST = name + (count) - 1 | ||||
/** References binary files compiled into the program. | /** References binary files compiled into the program. | ||||
For example, to include a file "Test.dat" directly into your program binary, add | For example, to include a file "Test.dat" directly into your program binary, add | ||||
BINARIES += Test.dat | BINARIES += Test.dat | ||||
to your Makefile and declare | to your Makefile and declare | ||||
BINARY(Test_dat); | BINARY(Test_dat); | ||||
at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | at the root of a .c or .cpp source file. Note that special characters are replaced with "_". Then use | ||||
BINARY_START(Test_dat) | BINARY_START(Test_dat) | ||||
BINARY_END(Test_dat) | BINARY_END(Test_dat) | ||||
to reference the data beginning and end as a void* array, and | to reference the data beginning and end as a void* array, and | ||||
BINARY_SIZE(Test_dat) | BINARY_SIZE(Test_dat) | ||||
to get its size in bytes. | to get its size in bytes. | ||||
*/ | */ | ||||
#if defined ARCH_MAC | #if defined ARCH_MAC | ||||
@@ -99,14 +116,15 @@ to get its size in bytes. | |||||
/** C#-style property constructor | /** C#-style property constructor | ||||
Example: | Example: | ||||
Foo *foo = construct<Foo>(&Foo::greeting, "Hello world", &Foo::legs, 2); | Foo *foo = construct<Foo>(&Foo::greeting, "Hello world", &Foo::legs, 2); | ||||
*/ | */ | ||||
template<typename T> | |||||
template <typename T> | |||||
T *construct() { | T *construct() { | ||||
return new T; | 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 *construct(F f, V v, Args... args) { | ||||
T *o = construct<T>(args...); | T *o = construct<T>(args...); | ||||
o->*f = v; | o->*f = v; | ||||
@@ -115,21 +133,21 @@ T *construct(F f, V v, Args... args) { | |||||
/** Defers code until the scope is destructed | /** Defers code until the scope is destructed | ||||
From http://www.gingerbill.org/article/defer-in-cpp.html | From http://www.gingerbill.org/article/defer-in-cpp.html | ||||
Example: | Example: | ||||
file = fopen(...); | file = fopen(...); | ||||
DEFER({ | DEFER({ | ||||
fclose(file); | fclose(file); | ||||
}); | }); | ||||
*/ | */ | ||||
template<typename F> | |||||
template <typename F> | |||||
struct DeferWrapper { | struct DeferWrapper { | ||||
F f; | F f; | ||||
DeferWrapper(F f) : f(f) {} | DeferWrapper(F f) : f(f) {} | ||||
~DeferWrapper() { f(); } | ~DeferWrapper() { f(); } | ||||
}; | }; | ||||
template<typename F> | |||||
template <typename F> | |||||
DeferWrapper<F> deferWrapper(F f) { | DeferWrapper<F> deferWrapper(F f) { | ||||
return 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) | #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 { | struct UserException : std::runtime_error { | ||||
UserException(const std::string &msg) : std::runtime_error(msg) {} | UserException(const std::string &msg) : std::runtime_error(msg) {} | ||||
}; | }; | ||||
@@ -300,9 +300,12 @@ struct SynthTechAlco : app::SvgKnob { | |||||
minAngle = -0.82*M_PI; | minAngle = -0.82*M_PI; | ||||
maxAngle = 0.82*M_PI; | maxAngle = 0.82*M_PI; | ||||
setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); | setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); | ||||
// Add cap | |||||
widget::FramebufferWidget *capFb = new widget::FramebufferWidget; | |||||
widget::SvgWidget *cap = new widget::SvgWidget; | widget::SvgWidget *cap = new widget::SvgWidget; | ||||
cap->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco_cap.svg"))); | 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 */ | /** Based on the size of 5mm LEDs */ | ||||
template <typename BASE> | template <typename BASE> | ||||
struct LargeLight : 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> | template <typename BASE> | ||||
struct LEDBezelLight : BASE { | struct LEDBezelLight : BASE { | ||||
LEDBezelLight() { | LEDBezelLight() { | ||||
@@ -608,15 +602,13 @@ struct PB61303 : app::SvgSwitch { | |||||
struct ScrewSilver : app::SvgScrew { | struct ScrewSilver : app::SvgScrew { | ||||
ScrewSilver() { | 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 { | struct ScrewBlack : app::SvgScrew { | ||||
ScrewBlack() { | 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> | #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 { | namespace rack { | ||||
@@ -52,8 +52,8 @@ TWidget *createWidget(math::Vec pos) { | |||||
template <class TWidget> | template <class TWidget> | ||||
TWidget *createWidgetCentered(math::Vec pos) { | 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; | return o; | ||||
} | } | ||||
@@ -2,14 +2,17 @@ | |||||
/** Example usage: | /** Example usage: | ||||
DEBUG("error: %d", errno); | DEBUG("error: %d", errno); | ||||
will print something like | will print something like | ||||
[0.123 debug myfile.cpp:45] error: 67 | [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 { | namespace rack { | ||||
@@ -29,8 +32,8 @@ enum Level { | |||||
void init(); | void init(); | ||||
void destroy(); | 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, ...); | 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. */ | /** Define this macro before including this header to prevent common namespaces from being included in the main `rack::` namespace. */ | ||||
#ifndef RACK_FLATTEN_NAMESPACES | #ifndef RACK_FLATTEN_NAMESPACES | ||||
// Import some namespaces for convenience | // Import some namespaces for convenience | ||||
using namespace logger; | |||||
using namespace math; | using namespace math; | ||||
using namespace widget; | using namespace widget; | ||||
using namespace ui; | 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. */ | /** 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::string fromWstring(const std::wstring &s); | ||||
std::wstring toWstring(const std::string &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, ...); | std::string f(const char *format, ...); | ||||
/** Replaces all characters to lowercase letters */ | /** Replaces all characters to lowercase letters */ | ||||
std::string lowercase(const std::string &s); | std::string lowercase(const std::string &s); | ||||
/** Replaces all characters to uppercase letters */ | /** Replaces all characters to uppercase letters */ | ||||
std::string uppercase(const std::string &s); | std::string uppercase(const std::string &s); | ||||
/** Removes whitespace from beginning and end of string. */ | |||||
std::string trim(const std::string &s); | std::string trim(const std::string &s); | ||||
/** Truncates and adds "..." to a string, not exceeding `len` characters */ | /** Truncates and adds "..." to a string, not exceeding `len` characters */ | ||||
std::string ellipsize(const std::string &s, size_t len); | 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. */ | /** 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. */ | /** Returns whether the given path is a file. */ | ||||
bool isFile(const std::string &path); | bool isFile(const std::string &path); | ||||
/** Returns whether the given path is a directory. */ | /** Returns whether the given path is a directory. */ | ||||
bool isDirectory(const std::string &path); | bool isDirectory(const std::string &path); | ||||
/** Moves a file. */ | |||||
void moveFile(const std::string &srcPath, const std::string &destPath); | |||||
/** Copies a file. */ | /** Copies a file. */ | ||||
void copyFile(const std::string &srcPath, const std::string &destPath); | void copyFile(const std::string &srcPath, const std::string &destPath); | ||||
/** Creates a directory. | /** 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. | May block, so open in a new thread. | ||||
*/ | */ | ||||
void openBrowser(const std::string &url); | 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); | void openFolder(const std::string &path); | ||||
/** Runs an executable without blocking. | /** Runs an executable without blocking. | ||||
The launched process will continue running if the current process is closed. | 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(); | std::string getOperatingSystemInfo(); | ||||
@@ -14,27 +14,6 @@ | |||||
#include <nanosvg.h> | #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 { | namespace rack { | ||||
@@ -6,8 +6,18 @@ namespace app { | |||||
SvgScrew::SvgScrew() { | SvgScrew::SvgScrew() { | ||||
fb = new widget::FramebufferWidget; | |||||
addChild(fb); | |||||
sw = new widget::SvgWidget; | 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)); | json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); | ||||
std::fclose(file); | std::fclose(file); | ||||
std::remove(path.c_str()); | |||||
std::rename(tmpPath.c_str(), path.c_str()); | |||||
system::moveFile(tmpPath, path); | |||||
} | } | ||||
void PatchManager::saveDialog() { | void PatchManager::saveDialog() { | ||||
@@ -149,7 +149,7 @@ static bool loadPlugin(std::string path) { | |||||
// Search for presets | // Search for presets | ||||
for (Model *model : plugin->models) { | for (Model *model : plugin->models) { | ||||
std::string presetDir = asset::plugin(plugin, "presets/" + model->slug); | 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); | model->presetPaths.push_back(presetPath); | ||||
} | } | ||||
} | } | ||||
@@ -190,7 +190,7 @@ static bool syncUpdate(const Update &update) { | |||||
static void loadPlugins(std::string path) { | static void loadPlugins(std::string path) { | ||||
std::string message; | std::string message; | ||||
for (std::string pluginPath : system::listEntries(path)) { | |||||
for (std::string pluginPath : system::getEntries(path)) { | |||||
if (!system::isDirectory(pluginPath)) | if (!system::isDirectory(pluginPath)) | ||||
continue; | continue; | ||||
if (!loadPlugin(pluginPath)) { | if (!loadPlugin(pluginPath)) { | ||||
@@ -271,7 +271,7 @@ static int extractZip(const char *filename, const char *path) { | |||||
static void extractPackages(const std::string &path) { | static void extractPackages(const std::string &path) { | ||||
std::string message; | std::string message; | ||||
for (std::string packagePath : system::listEntries(path)) { | |||||
for (std::string packagePath : system::getEntries(path)) { | |||||
if (string::filenameExtension(packagePath) != "zip") | if (string::filenameExtension(packagePath) != "zip") | ||||
continue; | continue; | ||||
INFO("Extracting package %s", packagePath.c_str()); | INFO("Extracting package %s", packagePath.c_str()); | ||||
@@ -25,7 +25,7 @@ namespace rack { | |||||
namespace system { | 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; | std::list<std::string> filenames; | ||||
DIR *dir = opendir(path.c_str()); | DIR *dir = opendir(path.c_str()); | ||||
if (dir) { | if (dir) { | ||||
@@ -56,14 +56,23 @@ bool isDirectory(const std::string &path) { | |||||
return S_ISDIR(statbuf.st_mode); | 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) { | void copyFile(const std::string &srcPath, const std::string &destPath) { | ||||
// Open files | |||||
// Open source | |||||
FILE *source = fopen(srcPath.c_str(), "rb"); | FILE *source = fopen(srcPath.c_str(), "rb"); | ||||
if (!source) | if (!source) | ||||
return; | return; | ||||
DEFER({ | DEFER({ | ||||
fclose(source); | fclose(source); | ||||
}); | }); | ||||
// Open destination | |||||
FILE *dest = fopen(destPath.c_str(), "wb"); | FILE *dest = fopen(destPath.c_str(), "wb"); | ||||
if (!dest) | if (!dest) | ||||
return; | return; | ||||
@@ -93,7 +102,6 @@ void createDirectory(const std::string &path) { | |||||
} | } | ||||
int getLogicalCoreCount() { | int getLogicalCoreCount() { | ||||
// TODO Return the physical cores, not logical cores. | |||||
return std::thread::hardware_concurrency(); | 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 | #if defined ARCH_WIN | ||||
STARTUPINFOW startupInfo; | STARTUPINFOW startupInfo; | ||||
PROCESS_INFORMATION processInfo; | PROCESS_INFORMATION processInfo; | ||||
@@ -212,6 +220,9 @@ void runProcessAsync(const std::string &path) { | |||||
CreateProcessW(pathW.c_str(), NULL, | CreateProcessW(pathW.c_str(), NULL, | ||||
NULL, NULL, false, 0, NULL, NULL, | NULL, NULL, false, 0, NULL, NULL, | ||||
&startupInfo, &processInfo); | &startupInfo, &processInfo); | ||||
#else | |||||
// Not implemented on Linux or Mac | |||||
assert(0); | |||||
#endif | #endif | ||||
} | } | ||||
@@ -226,6 +237,7 @@ std::string getOperatingSystemInfo() { | |||||
ZeroMemory(&info, sizeof(info)); | ZeroMemory(&info, sizeof(info)); | ||||
info.dwOSVersionInfoSize = sizeof(info); | info.dwOSVersionInfoSize = sizeof(info); | ||||
GetVersionExW(&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); | return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion); | ||||
#endif | #endif | ||||
} | } | ||||