[cairo-commit] [cairo-www] src/threaded_animation_with_cairo.mdwn
Carl Worth
cworth at freedesktop.org
Thu Jan 17 19:16:12 PST 2008
src/threaded_animation_with_cairo.mdwn | 1 -
1 file changed, 1 deletion(-)
New commits:
commit 55805118ee66a9a8c643be637d75d5969c4a7025
Author: Carl Worth <cworth at freedesktop.org>
Date: Thu Jan 17 19:16:11 2008 -0800
web commit by Albert
diff --git a/src/threaded_animation_with_cairo.mdwn b/src/threaded_animation_with_cairo.mdwn
index 78206b9..09522f8 100644
--- a/src/threaded_animation_with_cairo.mdwn
+++ b/src/threaded_animation_with_cairo.mdwn
@@ -38,4 +38,88 @@ A minimal thread-aware gtk program might look like:
gdk_threads_leave();
return 0;
- }
\ No newline at end of file
+ }
+
+##Setting up Callbacks for Animation
+We will use a `g_timeout_add` to call our `do_draw()` routine at 30 fps. Eventually, our `do_draw` will draw to a global `GdkPixmap` and we will paint this pixmap to the screen upon an `expose_event`. It is considered good practice to draw on a widget during its `expose_event` only.
+
+ #include <gtk/gtk.h>
+ #include <unistd.h>
+ #include <pthread.h>
+
+ //the global pixmap that will serve as our buffer
+ static GdkPixmap *pixmap = NULL;
+
+ int main (int argc, char *argv[]){
+
+
+ //we need to initialize all these functions so that gtk knows
+ //to be thread-aware
+ g_thread_init(NULL);
+ gdk_threads_init();
+ gdk_threads_enter();
+
+ gtk_init(&argc, &argv);
+
+ GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
+ g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(on_window_expose_event), NULL);
+ g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(on_window_configure_event), NULL);
+
+ //this must be done before we define our pixmap so that it can reference
+ //the colour depth and such
+ gtk_widget_show_all(window);
+
+ //set up our pixmap so it is ready for drawing
+ pixmap = gdk_pixmap_new(window->window,500,500,-1);
+ //because we will be painting our pixmap manually during expose events
+ //we can turn off gtk's automatic painting and double buffering routines.
+ gtk_widget_set_app_paintable(window, TRUE);
+ gtk_widget_set_double_buffered(window, FALSE);
+
+ (void)g_timeout_add(33, (GSourceFunc)timer_exe, window);
+
+
+ gtk_main();
+ gdk_threads_leave();
+
+ return 0;
+ }
+
+ * `g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(on_window_configure_event), NULL)` adds a callback to the configure event so that we can detect and handle resizes.
+ * `gtk_widget_set_app_paintable(window, TRUE)` and `gtk_widget_set_double_buffered(window, FALSE)` tells gtk that we will be doing our own buffering of the window
+ * `(void)g_timeout_add(33, (GSourceFunc)timer_exe, window)` adds a timer that will be executed by `gtk_main()` 30 times a second (unless `gtk_main()` is too busy with other things). We pass it the name of the function we'd like to call `timer_exe` cast as a `GSourceFunc` and we also pass it a pointer to the object we'd like to draw on, this time
+`window`.
+
+##Timer Function
+Our timer function will be responsible for launching a new thread that executes our `do_draw()` function. It will then artifically send an expose event to our window so that
+it knows it should redraw (sending an expose event rather than doing the drawing ourself will allow gtk to process events closer tot he way it wants).
+
+Before, we implement our timer function, we must consider one important issue. If our drawing application is going to take a long time (longer than 1/30th of a second) then simply launching a new drawing thread every time the timer executes could result in a pile-up of threads and a lot of memory badness. To solve this problem, we will have a variable `currently_drawing`. If `currently_drawing=0` then it is safe to launch a drawing thread. If it isn't, we know we haven't finished the drawing we started last time! Using this solution instead of sending a signal from our thread when it finishes, results in simpler code and has the added benefit that our framerate is limited to whatever rate our timer is called at.
+
+ gboolean timer_exe(GtkWidget * window){
+
+ //use a safe function to get the value of currently_drawing so
+ //we don't run into the usual multithreading issues
+ int drawing_status = g_atomic_int_get(¤tly_drawing);
+
+ //if we are not currently drawing anything, launch a thread to
+ //update our pixmap
+ if(drawing_status == 0){
+ pthread_t thread_info;
+ int iret;
+ iret = pthread_create( &thread_info, NULL, do_draw, NULL);
+ }
+
+ //tell our window it is time to draw our animation.
+ int width, height;
+ gdk_drawable_get_size(pixmap, &width, &height);
+ gtk_widget_queue_draw_area(window, 0, 0, width, height);
+
+ return TRUE;
+
+ }
+
+ * `g_atomic_int_get(¤tly_drawing)` is a thread-safe way to get the value of our global integer `currently_drawing`. Using this function allows us to avoid the possibility of reading a number at the same moment our other thread is trying to change it. It is also much easier to implement than mutexes for reading a single integer.
+ * `pthread_create( &thread_info, NULL, do_draw, NULL)` is the `unistd.h` way to launch the function `do_draw()` as a separate thread. (The final `NULL` is actually a `(void *)` to a data structure that we pass to `do_draw()`.)
+ * `gtk_widget_queue_draw_area(window, 0, 0, width, height)` sends an artificial expose event with upper left corner 0,0 and width and heigh of `width`, `height`, respectively. This allows our `expose_event` to do the actual painting.
\ No newline at end of file
More information about the cairo-commit
mailing list