delxa
delxa

Reputation: 99

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();
}

Upvotes: 1

Views: 1041

Answers (2)

Uli Schlachter
Uli Schlachter

Reputation: 9867

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.

Upvotes: 2

Zrax
Zrax

Reputation: 1670

According to GLib's documentation for g_timeout_add():

Note that timeout functions may be delayed, due to the processing of
other event sources. Thus they should not be relied on for precise
timing. After each call to the timeout function, the time of the next
timeout is recalculated based on the current time and the given
interval (it does not try to 'catch up' time lost in delays).

Because the event loop is doing other things for your application (including running your draw() function), the timeout may not be able to keep up with a 1ms tick, and certainly shouldn't be relied upon for precise timing.

Upvotes: 2

Related Questions