How to request redraw of Cinder window

Hi there. I’d like to have a UI Cinder app (e.g. using ImGui) that is set to a slow (ideally 0 :wink: ) refresh rate, e.g.

    app::setFrameRate(4);

Then, I’d like certain behaviours (like mouse click or drag, or when OSC messages arrive) to request window refreshes, so the response is quick in those cases. I don’t see an obvious (or cross-platform) way to request an update/redraw of the window. I thought maybe I could “emit” a request redraw message somehow in a generic way, but this isn’t (for example) what emitDraw() does, from what I can tell.

I’ve managed to find platform-specific workarounds, but was very curious if there’s a Cinder “built-in” way to do this nicely. Here’s my version, for Windows at least:

namespace {
    void redraw(app::WindowRef const& win)
    {
#if defined( CINDER_MSW )
        HWND hwnd = static_cast<HWND>(win->getNative());
        ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
#elif defined( CINDER_MAC )
        // NSView ...
#elif defined( CINDER_LINUX )
        // ...
#endif // CINDER_MSW
    }
}

void MyApp::mouseDown(app::MouseEvent event)
{
    ::redraw(event.getWindow());
}

void MyApp::keyDown(app::KeyEvent event)
{
    ::redraw(event.getWindow());
}

Thanks,
Glen.

Hi Glen,

it could be as simple as having a boolean that controls whether the window should redraw or not:

class YourApp : public App {
  public:
    void draw() override;
    void mouseDown( MouseEvent event ) override;
    void redraw();

  private:
    boolean mShouldRedraw = true;
};

void YourApp::draw()
{
    if( mShouldRedraw ) {
        redraw();

        mShouldRedraw = false;
    }
}

void YourApp::mouseDown( MouseEvent event )
{
    mShouldRedraw = true;
}

void YourApp::redraw()
{
    // Clear the current contents.
    gl::clear();

    // ...draw your UI...
}

Note that as long as you don’t clear the window, the contents will remain visible.

-Paul

Thanks, but I don’t see how that could work; won’t we just get flipping between the last two framebuffers in that case (Cinder will still do a “swap” after calling draw())… And anyhow, the behaviour should be system-dependent, because some systems may not maintain valid buffers when drawing with another window (partly) on top, or may copy and invalidate the back buffer after “swapping”). At least that’s how things used to work in the “old days” – there might be a trick I’m missing to get it to work. (-;

Do you have an example? I made one (basically what you suggested above), and indeed (on Windows) I get what I expected – flickering between the previous and next buffers.

Thanks,
Glen.

Here’s a complete example that (on Windows) shows the flickering between two different buffers when simply skipping drawing in the draw() method as you suggest…

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;

class YourApp : public App {
public:
    void draw() override;
    void mouseDown(MouseEvent event) override;
    void redraw();

private:
    bool    mShouldRedraw = true;
    Font    mFont{ "Arial", 36 };
};

void YourApp::draw()
{
    if (mShouldRedraw) {
        redraw();
    }
}

void YourApp::mouseDown(MouseEvent event)
{
    mShouldRedraw = true;
}

void YourApp::redraw()
{
    mShouldRedraw = false;

    // Clear the current contents.
    gl::clear(Color::black());

    static uint32_t drawNum = 0;
    auto const sz = getWindowSize();
    gl::drawStringCentered(std::to_string(app::getElapsedFrames()),
        vec2(20 + (drawNum % 2) * (sz.x - 40), 20 + (drawNum / 2) * (sz.y - 60)),
        Color(0.6f, 0.9f, 0.2f),
        mFont);
    drawNum = (drawNum + 1) % 4;
    // ...draw your UI...
}

CINDER_APP(YourApp, app::RendererGl, [](app::App::Settings* settings) {
    settings->setWindowSize(320, 240);
    settings->setWindowPos(40, 40);
    settings->setFrameRate(30);
    })