| @@ -32,6 +32,8 @@ struct FramebufferWidget : Widget { | |||
| NVGLUframebuffer* getFramebuffer(); | |||
| math::Vec getFramebufferSize(); | |||
| 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) { | |||
| 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 | |||
| /** 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 { | |||
| using Base = widget::BaseEvent; | |||
| @@ -50,7 +50,7 @@ void FramebufferWidget::setDirty(bool dirty) { | |||
| void FramebufferWidget::onDirty(const DirtyEvent& e) { | |||
| dirty = true; | |||
| setDirty(); | |||
| Widget::onDirty(e); | |||
| } | |||
| @@ -71,7 +71,7 @@ void FramebufferWidget::step() { | |||
| return; | |||
| // 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; | |||
| 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 drawing to a new subpixel location, rerender in the next frame. | |||
| // DEBUG("%p dirty subpixel", this); | |||
| dirty = true; | |||
| setDirty(); | |||
| } | |||
| if (!internal->scale.equals(internal->fbScale)) { | |||
| // If rescaled, rerender in the next frame. | |||
| // DEBUG("%p dirty scale", this); | |||
| dirty = true; | |||
| setDirty(); | |||
| } | |||
| 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 rack | |||
| @@ -327,6 +327,11 @@ Window::Window() { | |||
| // Load default Blendish font | |||
| uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | |||
| 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); | |||
| } | |||
| if (APP->scene) { | |||
| widget::Widget::ContextDestroyEvent e; | |||
| APP->scene->onContextDestroy(e); | |||
| } | |||
| #if defined NANOVG_GL2 | |||
| nvgDeleteGL2(vg); | |||
| #elif defined NANOVG_GL3 | |||
| @@ -422,30 +432,32 @@ void Window::step() { | |||
| glfwGetWindowSize(win, &winWidth, &winHeight); | |||
| 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); | |||
| @@ -223,6 +223,12 @@ int main(int argc, char* argv[]) { | |||
| INFO("Running window"); | |||
| APP->window->run(); | |||
| INFO("Stopped window"); | |||
| delete APP->window; | |||
| APP->window = NULL; | |||
| INFO("Re-creating window"); | |||
| APP->window = new Window; | |||
| APP->window->run(); | |||
| } | |||
| // Destroy context | |||