#include "global_pre.hpp" #include "window.hpp" #include "app.hpp" #include "asset.hpp" #include "settings.hpp" #include "gamepad.hpp" #include "keyboard.hpp" #include "util/color.hpp" #include #include #include #include "osdialog.h" #define NANOVG_GL2_IMPLEMENTATION 1 // #define NANOVG_GL3_IMPLEMENTATION 1 // #define NANOVG_GLES2_IMPLEMENTATION 1 // #define NANOVG_GLES3_IMPLEMENTATION 1 #include "nanovg_gl.h" // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported) #define NANOVG_FBO_VALID 1 #include "nanovg_gl_utils.h" #define BLENDISH_IMPLEMENTATION #include "blendish.h" #define NANOSVG_IMPLEMENTATION #define NANOSVG_ALL_COLOR_KEYWORDS #include "nanosvg.h" #ifdef ARCH_MAC // For CGAssociateMouseAndMouseCursorPosition #include #endif #include "global.hpp" #include "global_ui.hpp" #ifndef RACK_PLUGIN_SHARED_LIB_BUILD extern void vst2_set_globals (void *_wrapper); extern "C" { extern void lglw_timer_cbk (lglw_t _lglw); } // implemented in vst2_main.cpp extern "C" { extern void lglw_redraw_cbk (lglw_t _lglw); } // implemented in vst2_main.cpp #else void vst2_set_globals(void *) { } void vst2_window_size_set(int, int) { } void vst2_refresh_rate_set(float) { } void lglw_timer_cbk(lglw_t) { } void lglw_redraw_cbk(lglw_t) { } #endif // RACK_PLUGIN_SHARED_LIB_BUILD namespace rack { extern "C" { static void lglw_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) { // printf("xxx lglw_mouse_cbk: lglw=%p wrapper=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, lglw_userdata_get(_lglw), _x, _y, _buttonState, _changedButtonState); vst2_set_globals(lglw_userdata_get(_lglw)); // printf("xxx lglw_mouse_cbk: &global=%p global=%p\n", &rack::global, rack::global); // (note) assumes that GL context is never touched during regular mouse move // (note) mouse clicks may cause new SVGs to be loaded, which in turn may cause new GL textures to be created // if(0u != _changedButtonState) lglw_glcontext_push(global_ui->window.lglw); if(LGLW_MOUSE_WHEELUP == _buttonState) { // onScroll EventScroll e; e.pos = global_ui->window.gMousePos; Vec scrollRel = Vec(0, 1); e.scrollRel = scrollRel.mult(50.0); global_ui->ui.gScene->onScroll(e); } else if(LGLW_MOUSE_WHEELDOWN == _buttonState) { // onScroll EventScroll e; e.pos = global_ui->window.gMousePos; Vec scrollRel = Vec(0, -1); e.scrollRel = scrollRel.mult(50.0); global_ui->ui.gScene->onScroll(e); } else if(0u == _changedButtonState) { // onMouseMotion Vec mousePos = Vec(_x, _y);//.div(global_ui->window.gPixelRatio / global_ui->window.gWindowRatio).round(); Vec mouseRel = mousePos.minus(global_ui->window.gMousePos); // #ifdef ARCH_MAC // // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own. // // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped. // if (cursorMode == GLFW_CURSOR_HIDDEN) { // // CGSetLocalEventsSuppressionInterval(0.0); // glfwSetCursorPos(global_ui->window.gWindow, global_ui->window.gMousePos.x, global_ui->window.gMousePos.y); // CGAssociateMouseAndMouseCursorPosition(true); // mousePos = global_ui->window.gMousePos; // } // // Because sometimes the cursor turns into an arrow when its position is on the boundary of the window // glfwSetCursor(global_ui->window.gWindow, NULL); // #endif global_ui->window.gMousePos = mousePos; global_ui->widgets.gTempWidget = NULL; // onMouseMove { EventMouseMove e; e.pos = mousePos; e.mouseRel = mouseRel; global_ui->ui.gScene->onMouseMove(e); global_ui->widgets.gTempWidget = e.target; } if (global_ui->widgets.gDraggedWidget) { // onDragMove EventDragMove e; e.mouseRel = mouseRel; global_ui->widgets.gDraggedWidget->onDragMove(e); if (global_ui->widgets.gTempWidget != global_ui->widgets.gDragHoveredWidget) { if (global_ui->widgets.gDragHoveredWidget) { EventDragEnter e; e.origin = global_ui->widgets.gDraggedWidget; global_ui->widgets.gDragHoveredWidget->onDragLeave(e); } global_ui->widgets.gDragHoveredWidget = global_ui->widgets.gTempWidget; if (global_ui->widgets.gDragHoveredWidget) { EventDragEnter e; e.origin = global_ui->widgets.gDraggedWidget; global_ui->widgets.gDragHoveredWidget->onDragEnter(e); } } } else { if (global_ui->widgets.gTempWidget != global_ui->widgets.gHoveredWidget) { if (global_ui->widgets.gHoveredWidget) { // onMouseLeave EventMouseLeave e; global_ui->widgets.gHoveredWidget->onMouseLeave(e); } global_ui->widgets.gHoveredWidget = global_ui->widgets.gTempWidget; if (global_ui->widgets.gHoveredWidget) { // onMouseEnter EventMouseEnter e; global_ui->widgets.gHoveredWidget->onMouseEnter(e); } } } global_ui->widgets.gTempWidget = NULL; if(0u != (_buttonState & LGLW_MOUSE_MBUTTON)) { // TODO // Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed EventScroll e; e.pos = mousePos; e.scrollRel = mouseRel; global_ui->ui.gScene->onScroll(e); } } else { // Mouse button state changed // #ifdef ARCH_MAC // // Ctrl-left click --> right click // if (button == GLFW_MOUSE_BUTTON_LEFT) { // if (glfwGetKey(global_ui->window.gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(global_ui->window.gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { // button = GLFW_MOUSE_BUTTON_RIGHT; // } // } // #endif int button = (0u != (_changedButtonState & LGLW_MOUSE_LBUTTON)) ? RACK_MOUSE_BUTTON_LEFT/*0*/ : (0u != (_changedButtonState & LGLW_MOUSE_RBUTTON)) ? RACK_MOUSE_BUTTON_RIGHT/*1*/ : (0u != (_changedButtonState & LGLW_MOUSE_MBUTTON)) ? RACK_MOUSE_BUTTON_MIDDLE/*2*/ : -1; bool bPressed = (0u != (_changedButtonState & _buttonState)); if (bPressed) { global_ui->widgets.gTempWidget = NULL; // onMouseDown { EventMouseDown e; e.pos = global_ui->window.gMousePos; e.button = button; global_ui->ui.gScene->onMouseDown(e); global_ui->widgets.gTempWidget = e.target; } if (RACK_MOUSE_BUTTON_LEFT/*0*/ == button) { if (global_ui->widgets.gTempWidget) { // onDragStart EventDragStart e; global_ui->widgets.gTempWidget->onDragStart(e); } global_ui->widgets.gDraggedWidget = global_ui->widgets.gTempWidget; if (global_ui->widgets.gTempWidget != global_ui->widgets.gFocusedWidget) { if (global_ui->widgets.gFocusedWidget) { // onDefocus EventDefocus e; global_ui->widgets.gFocusedWidget->onDefocus(e); } global_ui->widgets.gFocusedWidget = NULL; if (global_ui->widgets.gTempWidget) { // onFocus EventFocus e; global_ui->widgets.gTempWidget->onFocus(e); if (e.consumed) { global_ui->widgets.gFocusedWidget = global_ui->widgets.gTempWidget; } } } } global_ui->widgets.gTempWidget = NULL; } else { // onMouseUp global_ui->widgets.gTempWidget = NULL; { EventMouseUp e; e.pos = global_ui->window.gMousePos; e.button = button; global_ui->ui.gScene->onMouseUp(e); global_ui->widgets.gTempWidget = e.target; } if (RACK_MOUSE_BUTTON_LEFT/*0*/ == button) { if (global_ui->widgets.gDraggedWidget) { // onDragDrop EventDragDrop e; e.origin = global_ui->widgets.gDraggedWidget; global_ui->widgets.gTempWidget->onDragDrop(e); } // gDraggedWidget might have been set to null in the last event, recheck here if (global_ui->widgets.gDraggedWidget) { // onDragEnd EventDragEnd e; global_ui->widgets.gDraggedWidget->onDragEnd(e); } global_ui->widgets.gDraggedWidget = NULL; global_ui->widgets.gDragHoveredWidget = NULL; } global_ui->widgets.gTempWidget = NULL; } } // if(0u != _changedButtonState) lglw_glcontext_pop(global_ui->window.lglw); // printf("xxx lglw_mouse_cbk: LEAVE global=%p\n", rack::global); } static void lglw_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) { // printf("xxx lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState); vst2_set_globals(lglw_userdata_get(_lglw)); if(0u != (_changedFocusState & LGLW_FOCUS_MOUSE)) { if(0u != (_focusState & LGLW_FOCUS_MOUSE)) { // onMouseEnter } else { if (global_ui->widgets.gHoveredWidget) { // onMouseLeave EventMouseLeave e; global_ui->widgets.gHoveredWidget->onMouseLeave(e); } global_ui->widgets.gHoveredWidget = NULL; } } } static lglw_bool_t lglw_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) { // printf("xxx lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, (char)_vkey, _kmod, _bPressed); lglw_bool_t bHandled = LGLW_FALSE; vst2_set_globals(lglw_userdata_get(_lglw)); if( (0u == (_vkey & LGLW_VKEY_EXT)) && (0u == (_kmod & (LGLW_KMOD_LCTRL | LGLW_KMOD_RCTRL))) ) { // Unicode or ASCII character if(_bPressed) { if (global_ui->widgets.gFocusedWidget) { // onText EventText e; e.codepoint = _vkey; global_ui->widgets.gFocusedWidget->onText(e); if(e.consumed) return LGLW_TRUE; } } } if(_bPressed) { if (global_ui->widgets.gFocusedWidget) { // onKey EventKey e; e.key = _vkey; global_ui->widgets.gFocusedWidget->onKey(e); if (e.consumed) return LGLW_TRUE; } // onHoverKey EventHoverKey e; e.pos = global_ui->window.gMousePos; e.key = _vkey; global_ui->ui.gScene->onHoverKey(e); } return bHandled; } int vst2_handle_effeditkeydown(unsigned int _vkey) { // (note) only used for touch keyboard input lglw_bool_t bHandled = lglw_keyboard_cbk(rack::global_ui->window.lglw, _vkey, 0u/*kmod*/, LGLW_TRUE/*bPressed*/); lglw_keyboard_cbk(rack::global_ui->window.lglw, _vkey, 0u/*kmod*/, LGLW_FALSE/*bPressed*/); return bHandled; } void lglw_dropfiles_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _numFiles, const char **_pathNames) { // onPathDrop vst2_set_globals(lglw_userdata_get(_lglw)); lglw_glcontext_push(global_ui->window.lglw); EventPathDrop e; e.pos = Vec(_x, _y); for(uint32_t i = 0u; i < _numFiles; i++) { e.paths.push_back(_pathNames[i]); } global_ui->ui.gScene->onPathDrop(e); lglw_glcontext_pop(global_ui->window.lglw); } } // extern C void renderGui() { int width, height; // printf("xxx renderGui: ENTER\n"); lglw_window_size_get(global_ui->window.lglw, &width, &height); // printf("xxx renderGui: 1 lglw=%p size=(%d; %d)\n", global_ui->window.lglw, width, height); // Update and render nvgBeginFrame(global_ui->window.gVg, width, height, global_ui->window.gPixelRatio); // printf("xxx renderGui: 2\n"); nvgReset(global_ui->window.gVg); // printf("xxx renderGui: 3\n"); nvgScale(global_ui->window.gVg, global_ui->window.gPixelRatio, global_ui->window.gPixelRatio); // printf("xxx renderGui: gScene->draw() BEGIN\n"); // printf("xxx renderGui: 4\n"); global_ui->ui.gScene->draw(global_ui->window.gVg); // printf("xxx renderGui: gScene->draw() END\n"); // printf("xxx renderGui: 5\n"); glViewport(0, 0, width, height); // printf("xxx renderGui: 6\n"); glClearColor(0.0, 0.0, 0.0, 1.0); // printf("xxx renderGui: 7\n"); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // printf("xxx renderGui: 8\n"); nvgEndFrame(global_ui->window.gVg); // printf("xxx renderGui: LEAVE\n"); } void windowInit() { int err; printf("xxx vstrack_plugin:windowInit: ENTER\n"); // (note) the hidden LGLW context window must have the same size as the real window created later on settingsLoad(assetLocal("settings.json"), true/*bWindowSizeOnly*/); printf("xxx vstrack_plugin:windowInit: 2\n"); global_ui->window.lglw = lglw_init(global_ui->window.windowWidth, global_ui->window.windowHeight); printf("xxx vstrack_plugin:windowInit: 3\n"); lglw_userdata_set(global_ui->window.lglw, global->vst2.wrapper); printf("xxx vstrack_plugin:windowInit: 4\n"); global_ui->window.lastWindowTitle = ""; lglw_glcontext_push(global_ui->window.lglw); printf("xxx vstrack_plugin:windowInit: 5\n"); global_ui->pending_swap_interval = 1; printf("xxx vstrack_plugin:windowInit: 6\n"); lglw_mouse_callback_set (global_ui->window.lglw, &lglw_mouse_cbk); lglw_focus_callback_set (global_ui->window.lglw, &lglw_focus_cbk); lglw_keyboard_callback_set (global_ui->window.lglw, &lglw_keyboard_cbk); lglw_timer_callback_set (global_ui->window.lglw, &lglw_timer_cbk); lglw_dropfiles_callback_set (global_ui->window.lglw, &lglw_dropfiles_cbk); lglw_redraw_callback_set (global_ui->window.lglw, &lglw_redraw_cbk); printf("xxx vstrack_plugin:windowInit: 7\n"); // 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."); lglw_glcontext_pop(global_ui->window.lglw); exit(1); } printf("xxx vstrack_plugin:windowInit: 8\n"); // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. glGetError(); printf("xxx vstrack_plugin:windowInit: 9\n"); // Set up NanoVG int nvgFlags = NVG_ANTIALIAS; #if defined NANOVG_GL2 global_ui->window.gVg = nvgCreateGL2(nvgFlags); #elif defined NANOVG_GL3 global_ui->window.gVg = nvgCreateGL3(nvgFlags); #elif defined NANOVG_GLES2 global_ui->window.gVg = nvgCreateGLES2(nvgFlags); #endif printf("xxx vstrack_plugin:windowInit: 10\n"); assert(global_ui->window.gVg); #if defined NANOVG_GL2 global_ui->window.gFramebufferVg = nvgCreateGL2(nvgFlags); #elif defined NANOVG_GL3 global_ui->window.gFramebufferVg = nvgCreateGL3(nvgFlags); #elif defined NANOVG_GLES2 global_ui->window.gFramebufferVg = nvgCreateGLES2(nvgFlags); #endif printf("xxx vstrack_plugin:windowInit: 11\n"); assert(global_ui->window.gFramebufferVg); // Set up Blendish printf("xxx vstrack_plugin:windowInit: 12\n"); global_ui->window.gGuiFont = Font::load(assetGlobal("res/fonts/DejaVuSans.ttf")); printf("xxx vstrack_plugin:windowInit: 13\n"); bndSetFont(global_ui->window.gGuiFont->handle); printf("xxx vstrack_plugin:windowInit: 14\n"); windowSetTheme(nvgRGB(0x33, 0x33, 0x33), nvgRGB(0xf0, 0xf0, 0xf0)); printf("xxx vstrack_plugin:windowInit: 15\n"); lglw_glcontext_pop(global_ui->window.lglw); printf("xxx vstrack_plugin:windowInit: LEAVE\n"); } void windowDestroy() { printf("xxx vstrack_plugin: windowDestroy()\n"); lglw_glcontext_push(global_ui->window.lglw); global_ui->window.gGuiFont.reset(); #if defined NANOVG_GL2 nvgDeleteGL2(global_ui->window.gVg); #elif defined NANOVG_GL3 nvgDeleteGL3(global_ui->window.gVg); #elif defined NANOVG_GLES2 nvgDeleteGLES2(global_ui->window.gVg); #endif #if defined NANOVG_GL2 nvgDeleteGL2(global_ui->window.gFramebufferVg); #elif defined NANOVG_GL3 nvgDeleteGL3(global_ui->window.gFramebufferVg); #elif defined NANOVG_GLES2 nvgDeleteGLES2(global_ui->window.gFramebufferVg); #endif lglw_glcontext_pop(global_ui->window.lglw); lglw_exit(global_ui->window.lglw); global_ui->window.lglw = NULL; } void vst2_editor_redraw(void) { // (note) the GL context is set by the caller // printf("xxx vst2_editor_redraw: ENTER\n"); if(-1 != global_ui->pending_swap_interval) { lglw_swap_interval_set(global_ui->window.lglw, global_ui->pending_swap_interval); // can be overridden via settings.json:"vsync" property global_ui->pending_swap_interval = -1; } global_ui->window.gGuiFrame++; // Find/validate hover param // printf("xxx vst2_editor_redraw: 2 global_ui->param_info.last_param_widget=%p\n", global_ui->param_info.last_param_widget); if(NULL != global_ui->param_info.last_param_widget) { int uniqueParamId; ParamWidget *paramWidget = global_ui->app.gRackWidget->findParamWidgetAndUniqueParamIdByWidgetRef(global_ui->param_info.last_param_widget, &uniqueParamId ); if(NULL != paramWidget) { global_ui->param_info.last_param_gid = uniqueParamId; global_ui->param_info.last_param_value = paramWidget->value; #if 0 printf("xxx vst2_editor_redraw: param_info: uniqueParamId=%d value=%f clipboardValue=%f\n", global_ui->param_info.last_param_gid, global_ui->param_info.last_param_value, global_ui->param_info.value_clipboard ); #endif char buf[64]; sprintf(buf, "%d", global_ui->param_info.last_param_gid); global_ui->param_info.tf_id->setTextQuiet(buf); sprintf(buf, "%f", global_ui->param_info.last_param_value); // Delete trailing zeros { char *d = buf; while(0 != *d) d++; d--; if(d > buf) { while('0' == *d) { if(((d-1) > buf) && ('.' != d[-1])) *d-- = 0; else break; } } } global_ui->param_info.tf_value->setTextQuiet(buf); } global_ui->param_info.last_param_widget = NULL; global_ui->param_info.placeholder_framecount = 1; } else if(0 != global_ui->param_info.placeholder_framecount) { if(++global_ui->param_info.placeholder_framecount > (30*30)) { global_ui->param_info.tf_id->setTextQuiet(""); global_ui->param_info.tf_value->setTextQuiet(""); global_ui->param_info.placeholder_framecount = 0; } } // printf("xxx vst2_editor_redraw: 3\n"); #if 0 // Set window title // (note) the VST plugin editor window title is set by the VST host std::string windowTitle; windowTitle = global_ui->app.gApplicationName; windowTitle += " "; windowTitle += global_ui->app.gApplicationVersion; if (!global_ui->app.gRackWidget->lastPath.empty()) { windowTitle += " - "; windowTitle += stringFilename(global_ui->app.gRackWidget->lastPath); } if (windowTitle != global_ui->window.lastWindowTitle) { // // glfwSetWindowTitle(global_ui->window.gWindow, windowTitle.c_str()); global_ui->window.lastWindowTitle = windowTitle; } #endif // Get framebuffer/window ratio int width, height; lglw_window_size_get(global_ui->window.lglw, &width, &height); global_ui->window.gWindowRatio = 1.0f; global_ui->ui.gScene->box.size = Vec(width, height); // printf("xxx vst2_editor_redraw: 4\n"); // Step scene global_ui->ui.gScene->step(); // printf("xxx vst2_editor_redraw: 5\n"); // Render renderGui(); // printf("xxx vst2_editor_redraw: 6\n"); // Present glFlush(); // printf("xxx vst2_editor_redraw: 7\n"); lglw_swap_buffers(global_ui->window.lglw); // printf("xxx vst2_editor_redraw: LEAVE\n"); } void windowCursorLock() { if (global_ui->window.gAllowCursorLock) { lglw_mouse_grab(global_ui->window.lglw, LGLW_MOUSE_GRAB_WARP); } } void windowCursorUnlock() { if (global_ui->window.gAllowCursorLock) { lglw_mouse_ungrab(global_ui->window.lglw); } } bool windowIsModPressed() { return (0u != (lglw_keyboard_get_modifiers(global_ui->window.lglw) & (LGLW_KMOD_LCTRL | LGLW_KMOD_RCTRL))); } bool windowIsShiftPressed() { return (0u != (lglw_keyboard_get_modifiers(global_ui->window.lglw) & (LGLW_KMOD_LSHIFT | LGLW_KMOD_RSHIFT))); } Vec windowGetWindowSize() { int width, height; lglw_window_size_get(global_ui->window.lglw, &width, &height); return Vec(width, height); } void windowSetWindowSize(Vec size) { (void)size; // (note) not supported } Vec windowGetWindowPos() { int x, y; x = 0; y = 0; return Vec(x, y); } void windowSetWindowPos(Vec pos) { int x = pos.x; int y = pos.y; // (note) not supported (void)x; (void)y; } bool windowIsMaximized() { return true; } void windowSetTheme(NVGcolor bg, NVGcolor fg) { // Assume dark background and light foreground BNDwidgetTheme w; w.outlineColor = bg; w.itemColor = fg; w.innerColor = bg; w.innerSelectedColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30)); w.textColor = fg; w.textSelectedColor = fg; w.shadeTop = 0; w.shadeDown = 0; BNDtheme t; t.backgroundColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30)); t.regularTheme = w; t.toolTheme = w; t.radioTheme = w; t.textFieldTheme = w; t.optionTheme = w; t.choiceTheme = w; t.numberFieldTheme = w; t.sliderTheme = w; t.scrollBarTheme = w; t.tooltipTheme = w; t.menuTheme = w; t.menuItemTheme = w; t.sliderTheme.itemColor = bg; t.sliderTheme.innerColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50)); t.sliderTheme.innerSelectedColor = colorPlus(bg, nvgRGB(0x60, 0x60, 0x60)); t.textFieldTheme = t.sliderTheme; t.textFieldTheme.textColor = colorMinus(bg, nvgRGB(0x20, 0x20, 0x20)); t.textFieldTheme.textSelectedColor = t.textFieldTheme.textColor; t.scrollBarTheme.itemColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50)); t.scrollBarTheme.innerColor = bg; t.menuTheme.innerColor = colorMinus(bg, nvgRGB(0x10, 0x10, 0x10)); t.menuTheme.textColor = colorMinus(fg, nvgRGB(0x50, 0x50, 0x50)); t.menuTheme.textSelectedColor = t.menuTheme.textColor; bndSetTheme(t); } void windowSetFullScreen(bool fullScreen) { (void)fullScreen; // (note) not supported } bool windowGetFullScreen() { return false; } void windowClose(void) { // (note) not supported } //////////////////// // resources //////////////////// Font::Font(const std::string &filename) { printf("xxx vstrack_plugin: Font::Font\n"); handle = nvgCreateFont(global_ui->window.gVg, filename.c_str(), filename.c_str()); if (handle >= 0) { info("Loaded font %s", filename.c_str()); } else { warn("Failed to load font %s", filename.c_str()); } } Font::~Font() { // There is no NanoVG deleteFont() function yet, so do nothing } std::shared_ptr Font::load(const std::string &filename) { auto sp = global_ui->window.font_cache[filename].lock(); if (!sp) global_ui->window.font_cache[filename] = sp = std::make_shared(filename); return sp; } //////////////////// // Image //////////////////// Image::Image(const std::string &filename) { printf("xxx vstrack_plugin: Image::Image\n"); handle = nvgCreateImage(global_ui->window.gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); if (handle > 0) { info("Loaded image %s", filename.c_str()); } else { warn("Failed to load image %s", filename.c_str()); } } Image::~Image() { // TODO What if handle is invalid? nvgDeleteImage(global_ui->window.gVg, handle); } std::shared_ptr Image::load(const std::string &filename) { auto sp = global_ui->window.image_cache[filename].lock(); if (!sp) global_ui->window.image_cache[filename] = sp = std::make_shared(filename); return sp; } //////////////////// // SVG //////////////////// SVG::SVG(const std::string &filename) { printf("xxx vstrack: SVG::SVG\n"); // printf("xxx SVG::SVG: ENTER\n"); handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI); // printf("xxx SVG::SVG: handle=%p\n"); if (handle) { info("Loaded SVG %s", filename.c_str()); } else { warn("Failed to load SVG %s", filename.c_str()); } // printf("xxx SVG::SVG: LEAVE\n"); } SVG::~SVG() { nsvgDelete(handle); } std::shared_ptr SVG::load(const std::string &filename) { // printf("xxx SVG::load: ENTER\n"); auto sp = global_ui->window.svg_cache[filename].lock(); if (!sp) global_ui->window.svg_cache[filename] = sp = std::make_shared(filename); // printf("xxx SVG::load: RETURN\n"); return sp; } } // namespace rack