| @@ -32,6 +32,8 @@ struct FramebufferWidget : Widget { | |||||
| NVGLUframebuffer* getFramebuffer(); | NVGLUframebuffer* getFramebuffer(); | ||||
| math::Vec getFramebufferSize(); | math::Vec getFramebufferSize(); | ||||
| void setScale(math::Vec scale); | void setScale(math::Vec scale); | ||||
| void onContextCreate(const ContextCreateEvent& e) override; | |||||
| void onContextDestroy(const ContextDestroyEvent& e) override; | |||||
| }; | }; | ||||
| @@ -452,13 +452,33 @@ struct Widget : WeakBase { | |||||
| virtual void onHide(const HideEvent& e) { | virtual void onHide(const HideEvent& e) { | ||||
| recurseEvent(&Widget::onHide, e); | recurseEvent(&Widget::onHide, e); | ||||
| } | } | ||||
| /** Occurs after the Window, OpenGL context, and Nanovg are created. | |||||
| Recurses. | |||||
| */ | |||||
| struct ContextCreateEvent : BaseEvent { | |||||
| NVGcontext* vg; | |||||
| }; | |||||
| virtual void onContextCreate(const ContextCreateEvent& e) { | |||||
| recurseEvent(&Widget::onContextCreate, e); | |||||
| } | |||||
| /** Occurs before the Window, OpenGL context, and Nanovg are destroyed. | |||||
| Recurses. | |||||
| */ | |||||
| struct ContextDestroyEvent : BaseEvent { | |||||
| NVGcontext* vg; | |||||
| }; | |||||
| virtual void onContextDestroy(const ContextDestroyEvent& e) { | |||||
| recurseEvent(&Widget::onContextDestroy, e); | |||||
| } | |||||
| }; | }; | ||||
| } // namespace widget | } // namespace widget | ||||
| /** Deprecated Rack v1 event namespace. | /** Deprecated Rack v1 event namespace. | ||||
| Use `FooEvent` instead of `event::Foo` in new code. | |||||
| Use `FooEvent` (defined in widget::Widget) instead of `event::Foo` in new code. | |||||
| */ | */ | ||||
| namespace event { | namespace event { | ||||
| using Base = widget::BaseEvent; | using Base = widget::BaseEvent; | ||||
| @@ -50,7 +50,7 @@ void FramebufferWidget::setDirty(bool dirty) { | |||||
| void FramebufferWidget::onDirty(const DirtyEvent& e) { | void FramebufferWidget::onDirty(const DirtyEvent& e) { | ||||
| dirty = true; | |||||
| setDirty(); | |||||
| Widget::onDirty(e); | Widget::onDirty(e); | ||||
| } | } | ||||
| @@ -71,7 +71,7 @@ void FramebufferWidget::step() { | |||||
| return; | return; | ||||
| // In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here. | // In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here. | ||||
| dirty = false; | |||||
| setDirty(false); | |||||
| NVGcontext* vg = APP->window->vg; | NVGcontext* vg = APP->window->vg; | ||||
| internal->fbScale = internal->scale; | internal->fbScale = internal->scale; | ||||
| @@ -186,12 +186,12 @@ void FramebufferWidget::draw(const DrawArgs& args) { | |||||
| if (dirtyOnSubpixelChange && !(math::isNear(internal->offsetF.x, internal->fbOffsetF.x, 0.01f) && math::isNear(internal->offsetF.y, internal->fbOffsetF.y, 0.01f))) { | if (dirtyOnSubpixelChange && !(math::isNear(internal->offsetF.x, internal->fbOffsetF.x, 0.01f) && math::isNear(internal->offsetF.y, internal->fbOffsetF.y, 0.01f))) { | ||||
| // If drawing to a new subpixel location, rerender in the next frame. | // If drawing to a new subpixel location, rerender in the next frame. | ||||
| // DEBUG("%p dirty subpixel", this); | // DEBUG("%p dirty subpixel", this); | ||||
| dirty = true; | |||||
| setDirty(); | |||||
| } | } | ||||
| if (!internal->scale.equals(internal->fbScale)) { | if (!internal->scale.equals(internal->fbScale)) { | ||||
| // If rescaled, rerender in the next frame. | // If rescaled, rerender in the next frame. | ||||
| // DEBUG("%p dirty scale", this); | // DEBUG("%p dirty scale", this); | ||||
| dirty = true; | |||||
| setDirty(); | |||||
| } | } | ||||
| math::Vec scaleRatio = math::Vec(1, 1); | math::Vec scaleRatio = math::Vec(1, 1); | ||||
| @@ -285,5 +285,18 @@ void FramebufferWidget::setScale(math::Vec scale) { | |||||
| } | } | ||||
| void FramebufferWidget::onContextCreate(const ContextCreateEvent& e) { | |||||
| setDirty(); | |||||
| Widget::onContextCreate(e); | |||||
| } | |||||
| void FramebufferWidget::onContextDestroy(const ContextDestroyEvent& e) { | |||||
| if (internal->fb) | |||||
| nvgluDeleteFramebuffer(internal->fb); | |||||
| Widget::onContextDestroy(e); | |||||
| } | |||||
| } // namespace widget | } // namespace widget | ||||
| } // namespace rack | } // namespace rack | ||||
| @@ -327,6 +327,11 @@ Window::Window() { | |||||
| // Load default Blendish font | // Load default Blendish font | ||||
| uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | ||||
| bndSetFont(uiFont->handle); | bndSetFont(uiFont->handle); | ||||
| if (APP->scene) { | |||||
| widget::Widget::ContextCreateEvent e; | |||||
| APP->scene->onContextCreate(e); | |||||
| } | |||||
| } | } | ||||
| @@ -344,6 +349,11 @@ Window::~Window() { | |||||
| settings::windowPos = math::Vec(winX, winY); | settings::windowPos = math::Vec(winX, winY); | ||||
| } | } | ||||
| if (APP->scene) { | |||||
| widget::Widget::ContextDestroyEvent e; | |||||
| APP->scene->onContextDestroy(e); | |||||
| } | |||||
| #if defined NANOVG_GL2 | #if defined NANOVG_GL2 | ||||
| nvgDeleteGL2(vg); | nvgDeleteGL2(vg); | ||||
| #elif defined NANOVG_GL3 | #elif defined NANOVG_GL3 | ||||
| @@ -422,30 +432,32 @@ void Window::step() { | |||||
| glfwGetWindowSize(win, &winWidth, &winHeight); | glfwGetWindowSize(win, &winWidth, &winHeight); | ||||
| windowRatio = (float)fbWidth / winWidth; | windowRatio = (float)fbWidth / winWidth; | ||||
| // DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth); | |||||
| // Resize scene | |||||
| APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio); | |||||
| // Step scene | |||||
| APP->scene->step(); | |||||
| // Render scene | |||||
| bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED); | |||||
| if (visible) { | |||||
| // Update and render | |||||
| nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); | |||||
| nvgScale(vg, pixelRatio, pixelRatio); | |||||
| // Draw scene | |||||
| widget::Widget::DrawArgs args; | |||||
| args.vg = vg; | |||||
| args.clipBox = APP->scene->box.zeroPos(); | |||||
| APP->scene->draw(args); | |||||
| 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); | |||||
| if (APP->scene) { | |||||
| // DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth); | |||||
| // Resize scene | |||||
| APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio); | |||||
| // Step scene | |||||
| APP->scene->step(); | |||||
| // Render scene | |||||
| bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED); | |||||
| if (visible) { | |||||
| // Update and render | |||||
| nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); | |||||
| nvgScale(vg, pixelRatio, pixelRatio); | |||||
| // Draw scene | |||||
| widget::Widget::DrawArgs args; | |||||
| args.vg = vg; | |||||
| args.clipBox = APP->scene->box.zeroPos(); | |||||
| APP->scene->draw(args); | |||||
| 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); | |||||
| } | |||||
| } | } | ||||
| glfwSwapBuffers(win); | glfwSwapBuffers(win); | ||||
| @@ -223,6 +223,12 @@ int main(int argc, char* argv[]) { | |||||
| INFO("Running window"); | INFO("Running window"); | ||||
| APP->window->run(); | APP->window->run(); | ||||
| INFO("Stopped window"); | INFO("Stopped window"); | ||||
| delete APP->window; | |||||
| APP->window = NULL; | |||||
| INFO("Re-creating window"); | |||||
| APP->window = new Window; | |||||
| APP->window->run(); | |||||
| } | } | ||||
| // Destroy context | // Destroy context | ||||