Search code examples
c++fltk

Is it possible to use standard C++ threads instead of FLTK timeouts to update window?


I need to create a simple animation that does itself without user interaction. There is a bunch of solutions that uses included FLTK timers to redraw the window in fixed time periods and that scheme is working fine without me moving the mouse or pressing keys, but as soon as it's changed to simple loop with redraw() in std::thread, everything breaks and redraw only works when events are being pushed to program by user.

Here's my code. If you bother launching it, you will see that updateFunc spams into the output each second steadily, but draw only appears there when you do something with mouse or keyboard and therefore the window changes its color. If that's normal behaviour, I need to change it somehow.

#include <bits/stdc++.h>
using namespace std;
#include <FL/Fl.h>
#include <FL/gl.h>
#include <FL/Fl_Gl_Window.h>
chrono::time_point<chrono::system_clock> execStart;
void log(const char* fmt, ...) {
    va_list arg;
    va_start(arg,fmt);
    fprintf(stderr,"[%10.3f] ",chrono::duration_cast<chrono::milliseconds>(
        chrono::system_clock::now()-execStart
    ).count()/1000.0);
    vfprintf(stderr,fmt,arg);
    fprintf(stderr,"\n");
    va_end(arg);
}

double rand01() {return (double)rand()/RAND_MAX;}

class Window: public Fl_Gl_Window {
private:
    thread updateThread;
    void updateFunc() {
        while (1) {
            log("%s",__func__);
            this_thread::sleep_for(1s);
            redraw();
        }
    }
protected:
    void draw() {
        log("%s",__func__);
        glClearColor(rand01(),rand01(),rand01(),1);
        glClear(GL_COLOR_BUFFER_BIT);
    }
public:
    Window(int x,int y,int w,int h,const char* s):
        Fl_Gl_Window(x,y,w,h,s),
        updateThread(&Window::updateFunc,this)
        {}
    Window(): Window(320,240,800,600,"FLTK OpenGL test") {}
};

int main() {
    execStart=chrono::system_clock::now();
    log("execution started");
    srand(time(0));

    Window *wnd=new Window();
    wnd->show();

    return Fl::run();
}

Solution

  • That’s normal behavior. But luckily for you, FLTK have it covered.

    In short, you must call Fl::lock() before starting your thread, and in your thread Fl::lock() before doing any updates, Fl::unlock() after updating the GUI, and Fl::awake() instead of redraw.

    Read the documentation for more info.