@@ -8,6 +8,8 @@ include dpf/Makefile.base.mk | |||
all: dgl plugins gen | |||
SKIP_NANOVG = true | |||
# -------------------------------------------------------------- | |||
dgl: | |||
@@ -40,9 +42,13 @@ clean: | |||
rm -rf plugins/CVCRack/Rack/dep/include | |||
rm -rf plugins/CVCRack/Rack/dep/lib | |||
rm -rf plugins/CVCRack/Rack/dep/share | |||
rm -rf plugins/CVCRack/Rack/dep/curl-7.66.0 | |||
rm -rf plugins/CVCRack/Rack/dep/glew-2.1.0 | |||
rm -rf plugins/CVCRack/Rack/dep/jansson-2.12 | |||
rm -rf plugins/CVCRack/Rack/dep/libarchive-3.4.3 | |||
rm -rf plugins/CVCRack/Rack/dep/openssl-1.1.1d | |||
rm -rf plugins/CVCRack/Rack/dep/speexdsp-SpeexDSP-1.2rc3 | |||
rm -rf plugins/CVCRack/Rack/dep/zstd-1.4.5 | |||
# -------------------------------------------------------------- | |||
@@ -1 +1 @@ | |||
Subproject commit 23f89562acbd637a23b9f0333877939ad26c0595 | |||
Subproject commit c17c260d08613ab46e13dc578104c74b5713a435 |
@@ -25,6 +25,9 @@ | |||
#include <settings.hpp> | |||
#include <system.hpp> | |||
#include <ui/common.hpp> | |||
#include <window/Window.hpp> | |||
#include <osdialog.h> | |||
#include "DistrhoPlugin.hpp" | |||
@@ -53,6 +56,7 @@ struct Initializer { | |||
// Load settings | |||
settings::init(); | |||
#if 0 | |||
try { | |||
settings::load(); | |||
} | |||
@@ -66,6 +70,7 @@ struct Initializer { | |||
} | |||
*/ | |||
} | |||
#endif | |||
// Check existence of the system res/ directory | |||
std::string resDir = asset::system("res"); | |||
@@ -88,12 +93,18 @@ struct Initializer { | |||
plugin::init(); | |||
library::init(); | |||
// discord::init(); | |||
ui::init(); | |||
window::init(); | |||
} | |||
~Initializer() | |||
{ | |||
using namespace rack; | |||
window::destroy(); | |||
ui::destroy(); | |||
// discord::destroy(); | |||
library::destroy(); | |||
midi::destroy(); | |||
@@ -104,9 +115,9 @@ struct Initializer { | |||
} | |||
}; | |||
static Initializer& getInitializerInstance() | |||
static const Initializer& getInitializerInstance() | |||
{ | |||
static Initializer init; | |||
static const Initializer init; | |||
return init; | |||
} | |||
@@ -23,6 +23,7 @@ | |||
#include <window/Window.hpp> | |||
#include "DistrhoUI.hpp" | |||
#include "ResizeHandle.hpp" | |||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; } | |||
GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char*) {} | |||
@@ -32,6 +33,10 @@ GLFWAPI int glfwGetKeyScancode(int key) { return 0; } | |||
namespace rack { | |||
namespace window { | |||
DISTRHO_NAMESPACE::UI* lastUI = nullptr; | |||
void mouseButtonCallback(Window* win, int button, int action, int mods); | |||
void cursorPosCallback(Window* win, double xpos, double ypos); | |||
void scrollCallback(Window* win, double x, double y); | |||
} | |||
} | |||
@@ -39,35 +44,33 @@ START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------------------------------------------- | |||
struct Initializer { | |||
Initializer() | |||
struct Initializer2 { | |||
Initializer2() | |||
{ | |||
using namespace rack; | |||
ui::init(); | |||
window::init(); | |||
} | |||
~Initializer() | |||
~Initializer2() | |||
{ | |||
using namespace rack; | |||
window::destroy(); | |||
ui::destroy(); | |||
} | |||
}; | |||
static Initializer& getInitializerInstance() | |||
static const Initializer2& getInitializer2Instance() | |||
{ | |||
static Initializer init; | |||
static const Initializer2 init; | |||
return init; | |||
} | |||
class CVCRackUI : public UI | |||
{ | |||
ResizeHandle fResizeHandle; | |||
public: | |||
CVCRackUI() | |||
: UI(1280, 720) | |||
: UI(1280, 720), | |||
fResizeHandle(this) | |||
{ | |||
using namespace rack; | |||
@@ -97,11 +100,16 @@ public: | |||
contextSet(NULL); | |||
} | |||
void onNanoDisplay() override | |||
void onDisplay() override | |||
{ | |||
APP->window->step(); | |||
} | |||
void uiIdle() override | |||
{ | |||
repaint(); | |||
} | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* DSP/Plugin Callbacks */ | |||
@@ -116,6 +124,61 @@ protected: | |||
// ------------------------------------------------------------------------------------------------------- | |||
bool onMouse(const MouseEvent& ev) override | |||
{ | |||
int button; | |||
int mods = 0; | |||
int action = ev.press; | |||
if (ev.mod & kModifierControl) | |||
mods |= GLFW_MOD_CONTROL; | |||
if (ev.mod & kModifierShift) | |||
mods |= GLFW_MOD_SHIFT; | |||
if (ev.mod & kModifierAlt) | |||
mods |= GLFW_MOD_ALT; | |||
switch (ev.button) | |||
{ | |||
case 0: | |||
button = GLFW_MOUSE_BUTTON_MIDDLE; | |||
break; | |||
case 1: | |||
button = GLFW_MOUSE_BUTTON_LEFT; | |||
break; | |||
case 2: | |||
button = GLFW_MOUSE_BUTTON_RIGHT; | |||
break; | |||
default: | |||
button = 0; | |||
break; | |||
} | |||
mouseButtonCallback(APP->window, button, action, mods); | |||
return true; | |||
} | |||
bool onMotion(const MotionEvent& ev) override | |||
{ | |||
cursorPosCallback(APP->window, ev.pos.getX(), ev.pos.getY()); | |||
return true; | |||
} | |||
bool onScroll(const ScrollEvent& ev) override | |||
{ | |||
scrollCallback(APP->window, ev.delta.getX(), ev.delta.getY()); | |||
return true; | |||
} | |||
#if 0 | |||
void onResize(const ResizeEvent& ev) override | |||
{ | |||
UI::onResize(ev); | |||
// APP->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); | |||
} | |||
#endif | |||
// TODO uiFocus | |||
private: | |||
/** | |||
Set our UI class as non-copyable and add a leak detector just in case. | |||
@@ -128,7 +191,7 @@ private: | |||
UI* createUI() | |||
{ | |||
getInitializerInstance(); | |||
getInitializer2Instance(); | |||
return new CVCRackUI(); | |||
} | |||
@@ -20,7 +20,7 @@ | |||
#define DISTRHO_PLUGIN_BRAND "DISTRHO" | |||
#define DISTRHO_PLUGIN_NAME "CVCRack" | |||
#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/plugins/glBars" | |||
#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/plugins/CVCRack" | |||
#define DISTRHO_PLUGIN_HAS_UI 1 | |||
#define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||
@@ -30,8 +30,8 @@ | |||
// #define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Analyzer" | |||
// #define DISTRHO_PLUGIN_HAS_EMBED_UI 1 | |||
// #define DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1 | |||
#define DISTRHO_UI_USE_NANOVG 1 | |||
#define DISTRHO_UI_USER_RESIZABLE 1 | |||
// #define DISTRHO_UI_USE_NANOVG 1 | |||
#define DISTRHO_UI_USER_RESIZABLE 0 | |||
enum Parameters { | |||
kParameterCount | |||
@@ -17,13 +17,13 @@ FILES_DSP = \ | |||
FILES_UI = \ | |||
CVCRackUI.cpp \ | |||
dep.cpp \ | |||
Window.cpp | |||
# -------------------------------------------------------------- | |||
# Import base definitions | |||
# UI_TYPE = external | |||
SKIP_NANOVG = true | |||
include ../../dpf/Makefile.base.mk | |||
# -------------------------------------------------------------- | |||
@@ -33,6 +33,7 @@ FILES_DSP += Rack/dep/pffft/pffft.c | |||
FILES_DSP += Rack/dep/pffft/fftpack.c | |||
FILES_UI += Rack/dep/oui-blendish/blendish.c | |||
FILES_UI += Rack/dep/nanovg/src/nanovg.c | |||
# FIXME dont use this | |||
FILES_UI += Rack/dep/osdialog/osdialog.c | |||
@@ -46,7 +47,7 @@ endif | |||
FILES_DSP += $(wildcard Rack/src/*.c) | |||
FILES_DSP += $(wildcard Rack/src/*/*.c) | |||
FILES_DSP += $(filter-out Rack/src/dep.cpp Rack/src/gamepad.cpp Rack/src/rtaudio.cpp Rack/src/rtmidi.cpp, $(wildcard Rack/src/*.cpp)) | |||
FILES_DSP += $(filter-out Rack/src/gamepad.cpp Rack/src/rtaudio.cpp Rack/src/rtmidi.cpp, $(wildcard Rack/src/*.cpp)) | |||
FILES_DSP += $(filter-out Rack/src/window/Window.cpp, $(wildcard Rack/src/*/*.cpp)) | |||
EXTRA_LIBS = Rack/dep/lib/libcrypto.a | |||
@@ -85,14 +86,14 @@ Rack/dep/lib/libcrypto.a: Rack/dep/lib/libssl.a | |||
# Extra flags for VCV stuff | |||
BASE_FLAGS += -D_APP_VERSION=2.git.0 | |||
BASE_FLAGS += -I$(DPF_PATH)/dgl/src/nanovg | |||
# BASE_FLAGS += -I$(DPF_PATH)/dgl/src/nanovg | |||
BASE_FLAGS += -IRack/include | |||
BASE_FLAGS += -IRack/dep/include | |||
BASE_FLAGS += -IRack/dep/filesystem/include | |||
BASE_FLAGS += -IRack/dep/fuzzysearchdatabase/src | |||
BASE_FLAGS += -IRack/dep/glfw/deps | |||
BASE_FLAGS += -IRack/dep/glfw/include | |||
# BASE_FLAGS += -IRack/dep/nanovg/src | |||
BASE_FLAGS += -IRack/dep/nanovg/src | |||
BASE_FLAGS += -IRack/dep/nanosvg/src | |||
BASE_FLAGS += -IRack/dep/osdialog | |||
BASE_FLAGS += -IRack/dep/oui-blendish | |||
@@ -0,0 +1,185 @@ | |||
/* | |||
* Resize handle for DPF | |||
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#pragma once | |||
#include "TopLevelWidget.hpp" | |||
#include "../dgl/Color.hpp" | |||
START_NAMESPACE_DGL | |||
/** Resize handle for DPF windows, will sit on bottom-right. */ | |||
class ResizeHandle : public TopLevelWidget | |||
{ | |||
public: | |||
/** Constructor for placing this handle on top of a window. */ | |||
explicit ResizeHandle(Window& window) | |||
: TopLevelWidget(window), | |||
handleSize(16), | |||
resizing(false) | |||
{ | |||
resetArea(); | |||
} | |||
/** Overloaded constructor, will fetch the window from an existing top-level widget. */ | |||
explicit ResizeHandle(TopLevelWidget* const tlw) | |||
: TopLevelWidget(tlw->getWindow()), | |||
handleSize(16), | |||
resizing(false) | |||
{ | |||
resetArea(); | |||
} | |||
/** Set the handle size, minimum 16. */ | |||
void setHandleSize(const uint size) | |||
{ | |||
handleSize = std::max(16u, size); | |||
resetArea(); | |||
} | |||
protected: | |||
void onDisplay() override | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
const double lineWidth = 1.0 * getScaleFactor(); | |||
#ifdef DGL_OPENGL | |||
glUseProgram(0); | |||
glMatrixMode(GL_MODELVIEW); | |||
#endif | |||
// draw white lines, 1px wide | |||
Color(1.0f, 1.0f, 1.0f).setFor(context); | |||
l1.draw(context, lineWidth); | |||
l2.draw(context, lineWidth); | |||
l3.draw(context, lineWidth); | |||
// draw black lines, offset by 1px and 1px wide | |||
Color(0.0f, 0.0f, 0.0f).setFor(context); | |||
Line<double> l1b(l1), l2b(l2), l3b(l3); | |||
l1b.moveBy(lineWidth, lineWidth); | |||
l2b.moveBy(lineWidth, lineWidth); | |||
l3b.moveBy(lineWidth, lineWidth); | |||
l1b.draw(context, lineWidth); | |||
l2b.draw(context, lineWidth); | |||
l3b.draw(context, lineWidth); | |||
} | |||
bool onMouse(const MouseEvent& ev) override | |||
{ | |||
if (ev.button != 1) | |||
return false; | |||
if (ev.press && area.contains(ev.pos)) | |||
{ | |||
resizing = true; | |||
resizingSize = Size<double>(getWidth(), getHeight()); | |||
lastResizePoint = ev.pos; | |||
return true; | |||
} | |||
if (resizing && ! ev.press) | |||
{ | |||
resizing = false; | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool onMotion(const MotionEvent& ev) override | |||
{ | |||
if (! resizing) | |||
return false; | |||
const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(), | |||
ev.pos.getY() - lastResizePoint.getY()); | |||
resizingSize += offset; | |||
lastResizePoint = ev.pos; | |||
// TODO min width, min height | |||
const uint minWidth = 16; | |||
const uint minHeight = 16; | |||
if (resizingSize.getWidth() < minWidth) | |||
resizingSize.setWidth(minWidth); | |||
if (resizingSize.getWidth() > 16384) | |||
resizingSize.setWidth(16384); | |||
if (resizingSize.getHeight() < minHeight) | |||
resizingSize.setHeight(minHeight); | |||
if (resizingSize.getHeight() > 16384) | |||
resizingSize.setHeight(16384); | |||
setSize(resizingSize.getWidth(), resizingSize.getHeight()); | |||
return true; | |||
} | |||
void onResize(const ResizeEvent& ev) override | |||
{ | |||
TopLevelWidget::onResize(ev); | |||
resetArea(); | |||
} | |||
private: | |||
Rectangle<uint> area; | |||
Line<double> l1, l2, l3; | |||
uint handleSize; | |||
// event handling state | |||
bool resizing; | |||
Point<double> lastResizePoint; | |||
Size<double> resizingSize; | |||
void resetArea() | |||
{ | |||
const double scaleFactor = getScaleFactor(); | |||
const uint margin = 0.0 * scaleFactor; | |||
const uint size = handleSize * scaleFactor; | |||
area = Rectangle<uint>(getWidth() - size - margin, | |||
getHeight() - size - margin, | |||
size, size); | |||
recreateLines(area.getX(), area.getY(), size); | |||
} | |||
void recreateLines(const uint x, const uint y, const uint size) | |||
{ | |||
uint linesize = size; | |||
uint offset = 0; | |||
// 1st line, full diagonal size | |||
l1.setStartPos(x + size, y); | |||
l1.setEndPos(x, y + size); | |||
// 2nd line, bit more to the right and down, cropped | |||
offset += size / 3; | |||
linesize -= size / 3; | |||
l2.setStartPos(x + linesize + offset, y + offset); | |||
l2.setEndPos(x + offset, y + linesize + offset); | |||
// 3rd line, even more right and down | |||
offset += size / 3; | |||
linesize -= size / 3; | |||
l3.setStartPos(x + linesize + offset, y + offset); | |||
l3.setEndPos(x + offset, y + linesize + offset); | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) | |||
}; | |||
END_NAMESPACE_DGL |
@@ -68,6 +68,7 @@ std::shared_ptr<Image> Image::load(const std::string& filename) { | |||
struct Window::Internal { | |||
DISTRHO_NAMESPACE::UI* ui; | |||
math::Vec size; | |||
std::string lastWindowTitle; | |||
@@ -94,11 +95,18 @@ struct Window::Internal { | |||
Window::Window() { | |||
internal = new Internal; | |||
internal->ui = lastUI; | |||
vg = lastUI->getContext(); | |||
internal->size = minWindowSize; | |||
int err; | |||
// Set up GLEW | |||
glewExperimental = GL_TRUE; | |||
err = glewInit(); | |||
if (err != GLEW_OK) { | |||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | |||
exit(1); | |||
} | |||
const GLubyte* vendor = glGetString(GL_VENDOR); | |||
const GLubyte* renderer = glGetString(GL_RENDERER); | |||
const GLubyte* version = glGetString(GL_VERSION); | |||
@@ -109,6 +117,21 @@ Window::Window() { | |||
// GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. | |||
glGetError(); | |||
// Set up NanoVG | |||
int nvgFlags = NVG_ANTIALIAS; | |||
#if defined NANOVG_GL2 | |||
vg = nvgCreateGL2(nvgFlags); | |||
fbVg = nvgCreateSharedGL2(vg, nvgFlags); | |||
#elif defined NANOVG_GL3 | |||
vg = nvgCreateGL3(nvgFlags); | |||
#elif defined NANOVG_GLES2 | |||
vg = nvgCreateGLES2(nvgFlags); | |||
#endif | |||
if (!vg) { | |||
osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize NanoVG. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); | |||
exit(1); | |||
} | |||
// Load default Blendish font | |||
uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | |||
bndSetFont(uiFont->handle); | |||
@@ -132,17 +155,26 @@ Window::~Window() { | |||
// nvgDeleteClone(fbVg); | |||
#if defined NANOVG_GL2 | |||
nvgDeleteGL2(vg); | |||
nvgDeleteGL2(fbVg); | |||
#elif defined NANOVG_GL3 | |||
nvgDeleteGL3(vg); | |||
#elif defined NANOVG_GLES2 | |||
nvgDeleteGLES2(vg); | |||
#endif | |||
delete internal; | |||
} | |||
math::Vec Window::getSize() { | |||
return math::Vec(1280, 720); | |||
return internal->size; | |||
} | |||
void Window::setSize(math::Vec size) { | |||
size = size.max(minWindowSize); | |||
internal->size = size.max(minWindowSize); | |||
} | |||
@@ -163,14 +195,12 @@ void Window::step() { | |||
nvgReset(vg); | |||
bndSetFont(uiFont->handle); | |||
nvgFillColor(vg, nvgRGBf(1, 1, 1)); | |||
nvgStrokeColor(vg, nvgRGBf(1, 1, 1)); | |||
// Poll events | |||
// Save and restore context because event handler set their own context based on which window they originate from. | |||
Context* context = contextGet(); | |||
// Context* context = contextGet(); | |||
// glfwPollEvents(); | |||
contextSet(context); | |||
// contextSet(context); | |||
// Set window title | |||
std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION; | |||
@@ -223,9 +253,9 @@ void Window::step() { | |||
APP->scene->draw(args); | |||
t3 = system::getTime(); | |||
// glViewport(0, -winHeight, fbWidth, fbHeight); | |||
// glClearColor(0.0, 0.0, 0.0, 1.0); | |||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||
glViewport(0, 0, fbWidth, fbHeight); | |||
glClearColor(0.0, 0.0, 0.0, 1.0); | |||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||
nvgEndFrame(vg); | |||
t4 = system::getTime(); | |||
} | |||
@@ -370,6 +400,56 @@ bool& Window::fbDirtyOnSubpixelChange() { | |||
} | |||
void mouseButtonCallback(Window* win, int button, int action, int mods) { | |||
/* | |||
#if defined ARCH_MAC | |||
// Remap Ctrl-left click to right click on Mac | |||
if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) { | |||
button = GLFW_MOUSE_BUTTON_RIGHT; | |||
mods &= ~GLFW_MOD_CONTROL; | |||
} | |||
// Remap Ctrl-shift-left click to middle click on Mac | |||
if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) { | |||
button = GLFW_MOUSE_BUTTON_MIDDLE; | |||
mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT); | |||
} | |||
#endif | |||
*/ | |||
APP->event->handleButton(win->internal->lastMousePos, button, action, mods); | |||
} | |||
void cursorPosCallback(Window* win, double xpos, double ypos) { | |||
math::Vec mousePos = math::Vec(xpos, ypos).div(win->pixelRatio / win->windowRatio).round(); | |||
math::Vec mouseDelta = mousePos.minus(win->internal->lastMousePos); | |||
// Workaround for GLFW warping mouse to a different position when the cursor is locked or unlocked. | |||
if (win->internal->ignoreNextMouseDelta) { | |||
win->internal->ignoreNextMouseDelta = false; | |||
mouseDelta = math::Vec(); | |||
} | |||
win->internal->lastMousePos = mousePos; | |||
APP->event->handleHover(mousePos, mouseDelta); | |||
// Keyboard/mouse MIDI driver | |||
math::Vec scaledPos(xpos / win->internal->ui->getWidth(), ypos / win->internal->ui->getHeight()); | |||
keyboard::mouseMove(scaledPos); | |||
} | |||
void scrollCallback(Window* win, double x, double y) { | |||
math::Vec scrollDelta = math::Vec(x, y); | |||
#if defined ARCH_MAC | |||
scrollDelta = scrollDelta.mult(10.0); | |||
#else | |||
scrollDelta = scrollDelta.mult(50.0); | |||
#endif | |||
APP->event->handleScroll(win->internal->lastMousePos, scrollDelta); | |||
} | |||
void init() { | |||
} | |||
@@ -1,19 +0,0 @@ | |||
// This source file compiles those annoying implementation-in-header libraries | |||
#include <common.hpp> // for fopen_u8 | |||
#define GLEW_STATIC | |||
#define GLEW_NO_GLU | |||
#include <GL/glew.h> | |||
#include <nanovg.h> | |||
#define BLENDISH_IMPLEMENTATION | |||
#include <blendish.h> | |||
#define NANOSVG_IMPLEMENTATION | |||
#define NANOSVG_ALL_COLOR_KEYWORDS | |||
#include <nanosvg.h> | |||
#define STB_IMAGE_WRITE_IMPLEMENTATION | |||
#include <stb_image_write.h> |