Search code examples
cgtk3cairo

Gtk3 and cairo g_timeout_add doesn't work


I've made very simple animation with gtk3 and cairo. I've got g_timeout_add and it should call draw function every 1ms, but it call draw much slower than should. Why it happens and how can I fix it?

#include <gtk/gtk.h>
#include <cairo.h>

gboolean timeout(GtkWidget *widget)
{
    gtk_widget_queue_draw(widget);
    return TRUE;
}

void draw(GtkWidget* widget, cairo_t* cr)
{
    static int width, height,
               posX = 0,
               vX = 1;

    GtkWidget* window = gtk_widget_get_toplevel(widget);
    gtk_window_get_size(GTK_WINDOW(window), &width, &height);

    cairo_set_source_rgb(cr, 0, 0, 0);
    cairo_set_line_width(cr, 1);

    cairo_rectangle(cr, posX, height/2, 1, 1);
    cairo_stroke(cr);

    if(posX + vX >= width || posX + vX == 0)
        vX = -vX;
    posX += vX;
}

int main(int argc, char** argv)
{
    GtkWidget* window;
    GtkWidget* darea;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    darea = gtk_drawing_area_new();

    gtk_container_add(GTK_CONTAINER(window), darea);

    gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);

    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(draw), NULL);

    g_timeout_add(1, (GSourceFunc)timeout, window);

    gtk_widget_show_all(window);
    gtk_main();
}

Solution

  • Why it happens and

    This was answered by Zrax. I just want to add something: I added the following to your draw function (and added the appropriate includes)

    struct timeval tv;
    gettimeofday(&tv, NULL);
    printf("%d.%06d\n", (int) tv.tv_sec, (int) tv.tv_usec);
    

    and I get output like the following:

    1518869317.202412
    1518869317.218970
    1518869317.236563
    1518869317.253032
    1518869317.269721
    1518869317.286305
    

    So your draw function is called about every 17 milliseconds, or about 60 times per second.

    how can I fix it?

    This raises the question: Why do you want to draw faster than 60 fps anyway?

    How about creating a GTimer and using g_timer_elapsed() to figure out where you have to draw? I.e. instead of assuming that you are drawing fast enough, you just compute the current graphics state each time you draw.