@@ -12,6 +12,7 @@ struct Knob : ParamWidget { | |||||
/** Multiplier for mouse movement to adjust knob value */ | /** Multiplier for mouse movement to adjust knob value */ | ||||
float speed = 1.0; | float speed = 1.0; | ||||
float oldValue = 0.f; | float oldValue = 0.f; | ||||
bool smooth = true; | |||||
/** Enable snapping at integer values */ | /** Enable snapping at integer values */ | ||||
bool snap = false; | bool snap = false; | ||||
float snapValue = NAN; | float snapValue = NAN; | ||||
@@ -16,11 +16,13 @@ struct ParamWidget : OpaqueWidget { | |||||
~ParamWidget(); | ~ParamWidget(); | ||||
void step() override; | void step() override; | ||||
/** For legacy patch loading */ | |||||
void fromJson(json_t *rootJ); | |||||
void draw(NVGcontext *vg) override; | |||||
void onButton(const event::Button &e) override; | void onButton(const event::Button &e) override; | ||||
void onEnter(const event::Enter &e) override; | void onEnter(const event::Enter &e) override; | ||||
void onLeave(const event::Leave &e) override; | void onLeave(const event::Leave &e) override; | ||||
/** For legacy patch loading */ | |||||
void fromJson(json_t *rootJ); | |||||
}; | }; | ||||
@@ -52,6 +52,7 @@ struct Key { | |||||
int mods; | int mods; | ||||
}; | }; | ||||
// Events | |||||
struct Text { | struct Text { | ||||
/** Unicode code point of the character */ | /** Unicode code point of the character */ | ||||
@@ -183,7 +183,7 @@ void CableWidget::draw(NVGcontext *vg) { | |||||
if (cable && cable->outputModule) { | if (cable && cable->outputModule) { | ||||
Output *output = &cable->outputModule->outputs[cable->outputId]; | Output *output = &cable->outputModule->outputs[cable->outputId]; | ||||
if (output->numChannels != 1) { | if (output->numChannels != 1) { | ||||
thickness = 9; | |||||
thickness = 7; | |||||
} | } | ||||
} | } | ||||
@@ -28,9 +28,9 @@ void Knob::onButton(const event::Button &e) { | |||||
void Knob::onDragStart(const event::DragStart &e) { | void Knob::onDragStart(const event::DragStart &e) { | ||||
if (paramQuantity) { | if (paramQuantity) { | ||||
oldValue = paramQuantity->getValue(); | |||||
oldValue = paramQuantity->getSmoothValue(); | |||||
if (snap) { | if (snap) { | ||||
snapValue = oldValue; | |||||
snapValue = paramQuantity->getValue(); | |||||
} | } | ||||
} | } | ||||
@@ -41,7 +41,7 @@ void Knob::onDragEnd(const event::DragEnd &e) { | |||||
app()->window->cursorUnlock(); | app()->window->cursorUnlock(); | ||||
if (paramQuantity) { | if (paramQuantity) { | ||||
float newValue = paramQuantity->getValue(); | |||||
float newValue = paramQuantity->getSmoothValue(); | |||||
if (oldValue != newValue) { | if (oldValue != newValue) { | ||||
// Push ParamChange history action | // Push ParamChange history action | ||||
history::ParamChange *h = new history::ParamChange; | history::ParamChange *h = new history::ParamChange; | ||||
@@ -75,9 +75,12 @@ void Knob::onDragMove(const event::DragMove &e) { | |||||
snapValue = math::clamp(snapValue, paramQuantity->getMinValue(), paramQuantity->getMaxValue()); | snapValue = math::clamp(snapValue, paramQuantity->getMinValue(), paramQuantity->getMaxValue()); | ||||
paramQuantity->setValue(std::round(snapValue)); | paramQuantity->setValue(std::round(snapValue)); | ||||
} | } | ||||
else { | |||||
else if (smooth) { | |||||
paramQuantity->setSmoothValue(paramQuantity->getSmoothValue() + delta); | paramQuantity->setSmoothValue(paramQuantity->getSmoothValue() + delta); | ||||
} | } | ||||
else { | |||||
paramQuantity->setValue(paramQuantity->getValue() + delta); | |||||
} | |||||
} | } | ||||
ParamWidget::onDragMove(e); | ParamWidget::onDragMove(e); | ||||
@@ -93,6 +93,22 @@ void ParamWidget::step() { | |||||
OpaqueWidget::step(); | OpaqueWidget::step(); | ||||
} | } | ||||
void ParamWidget::draw(NVGcontext *vg) { | |||||
Widget::draw(vg); | |||||
if (paramQuantity) { | |||||
nvgBeginPath(vg); | |||||
nvgRect(vg, | |||||
box.size.x - 12, box.size.y - 12, | |||||
12, 12); | |||||
nvgFillColor(vg, nvgRGBAf(1, 0, 1, 0.9)); | |||||
nvgFill(vg); | |||||
std::string mapText = string::f("%d", paramQuantity->paramId); | |||||
bndLabel(vg, box.size.x - 17.0, box.size.y - 16.0, INFINITY, INFINITY, -1, mapText.c_str()); | |||||
} | |||||
} | |||||
void ParamWidget::fromJson(json_t *rootJ) { | void ParamWidget::fromJson(json_t *rootJ) { | ||||
json_t *valueJ = json_object_get(rootJ, "value"); | json_t *valueJ = json_object_get(rootJ, "value"); | ||||
if (valueJ) { | if (valueJ) { | ||||
@@ -24,7 +24,7 @@ void SVGSwitch::addFrame(std::shared_ptr<SVG> svg) { | |||||
void SVGSwitch::onChange(const event::Change &e) { | void SVGSwitch::onChange(const event::Change &e) { | ||||
if (!frames.empty() && paramQuantity) { | if (!frames.empty() && paramQuantity) { | ||||
int index = (int) paramQuantity->getValue(); | |||||
int index = (int) std::round(paramQuantity->getValue()); | |||||
index = math::clamp(index, 0, (int) frames.size() - 1); | index = math::clamp(index, 0, (int) frames.size() - 1); | ||||
sw->setSVG(frames[index]); | sw->setSVG(frames[index]); | ||||
fb->dirty = true; | fb->dirty = true; | ||||
@@ -16,23 +16,23 @@ | |||||
#include <osdialog.h> | #include <osdialog.h> | ||||
#ifdef ARCH_WIN | #ifdef ARCH_WIN | ||||
#include <Windows.h> | |||||
#include <Windows.h> | |||||
#endif | #endif | ||||
using namespace rack; | using namespace rack; | ||||
int main(int argc, char *argv[]) { | int main(int argc, char *argv[]) { | ||||
#ifdef ARCH_WIN | |||||
// Windows global mutex to prevent multiple instances | |||||
// Handle will be closed by Windows when the process ends | |||||
HANDLE instanceMutex = CreateMutex(NULL, true, APP_NAME.c_str()); | |||||
if (GetLastError() == ERROR_ALREADY_EXISTS) { | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported."); | |||||
exit(1); | |||||
} | |||||
(void) instanceMutex; | |||||
#endif | |||||
#ifdef ARCH_WIN | |||||
// Windows global mutex to prevent multiple instances | |||||
// Handle will be closed by Windows when the process ends | |||||
HANDLE instanceMutex = CreateMutex(NULL, true, APP_NAME.c_str()); | |||||
if (GetLastError() == ERROR_ALREADY_EXISTS) { | |||||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Rack is already running. Multiple Rack instances are not supported."); | |||||
exit(1); | |||||
} | |||||
(void) instanceMutex; | |||||
#endif | |||||
bool devMode = false; | bool devMode = false; | ||||
std::string patchFile; | std::string patchFile; | ||||
@@ -59,7 +59,6 @@ int main(int argc, char *argv[]) { | |||||
} | } | ||||
// Initialize environment | // Initialize environment | ||||
random::init(); | |||||
asset::init(devMode); | asset::init(devMode); | ||||
logger::init(devMode); | logger::init(devMode); | ||||
@@ -70,6 +69,7 @@ int main(int argc, char *argv[]) { | |||||
INFO("System directory: %s", asset::systemDir.c_str()); | INFO("System directory: %s", asset::systemDir.c_str()); | ||||
INFO("User directory: %s", asset::userDir.c_str()); | INFO("User directory: %s", asset::userDir.c_str()); | ||||
random::init(); | |||||
midi::init(); | midi::init(); | ||||
rtmidiInit(); | rtmidiInit(); | ||||
bridgeInit(); | bridgeInit(); | ||||
@@ -77,6 +77,7 @@ int main(int argc, char *argv[]) { | |||||
gamepad::init(); | gamepad::init(); | ||||
ui::init(); | ui::init(); | ||||
plugin::init(devMode); | plugin::init(devMode); | ||||
INFO("Initialized environment") | |||||
// Initialize app | // Initialize app | ||||
appInit(); | appInit(); | ||||
@@ -104,21 +105,25 @@ int main(int argc, char *argv[]) { | |||||
app()->scene->rackWidget->load(patchFile); | app()->scene->rackWidget->load(patchFile); | ||||
app()->scene->rackWidget->lastPath = patchFile; | app()->scene->rackWidget->lastPath = patchFile; | ||||
} | } | ||||
INFO("Initialized app") | |||||
app()->engine->start(); | app()->engine->start(); | ||||
app()->window->run(); | app()->window->run(); | ||||
INFO("Window closed"); | |||||
app()->engine->stop(); | app()->engine->stop(); | ||||
// Destroy app | // Destroy app | ||||
app()->scene->rackWidget->save(asset::user("autosave.vcv")); | app()->scene->rackWidget->save(asset::user("autosave.vcv")); | ||||
settings::save(asset::user("settings.json")); | settings::save(asset::user("settings.json")); | ||||
appDestroy(); | appDestroy(); | ||||
INFO("Cleaned up app") | |||||
// Destroy environment | // Destroy environment | ||||
plugin::destroy(); | plugin::destroy(); | ||||
ui::destroy(); | ui::destroy(); | ||||
bridgeDestroy(); | bridgeDestroy(); | ||||
midi::destroy(); | midi::destroy(); | ||||
INFO("Cleaned up environment") | |||||
logger::destroy(); | logger::destroy(); | ||||
return 0; | return 0; | ||||