Hi again Stewart, On Dec 2, 2007 5:12 AM, Stewart Weiss <stewart.weiss@xxxxxxx> wrote: > I do still have one question about a specific suggestion that you made in > this thread, below: I've just spent a while writing you a sample rubberband program and now I come to post it I see Richard has done the same! Ah well, perhaps you can't have too much sample code. Mine is a little different from Richard's, so I'm going to paste it here anyway. Richard's is a retained mode program. He keeps a complete bitmap for his display in an offscreen buffer and does all animation there. On expose, he just copies the relevant part to the screen. Mine is a list-mode program. I have no backing pixmaps: I do all drawing in the expose handler. The display only exists as a few numbers for the positions of the images and the rubberband line. The two styles are probably appropriate for different type of program (as I guess our discussion showed). I suppose most programs will fall somewhere inbetween these two. If you try it out, run with something like: ./a.out ~/pics/*.jpg (or wherever you keep some pictures). It creates a window with the first 10 images bouncing around and lets you rubberband a white line that floats on top. It has the following nice properties: - the images animate smoothly, they float over each other in a clearly defined stacking order, and the rubberband line is always on top - the animation routine is very simple, since it does no drawing - because drawing and animation are decoupled, the speed stays constant even under load (the framerate just drops) - resizing is fluid and doesn't interrupt the animation, since there's no pixmap to rebuild - it uses motion hints so the rubberband doesn't lag --------------------------------- /* compile with * gcc -g -Wall try144.c `pkg-config gtk+-2.0 --cflags --libs` */ #include <stdio.h> #include <stdlib.h> #include <gtk/gtk.h> #define MAX_IMAGES (10) /* Application state. */ typedef struct _App { /* Drawingarea we draw to. */ GtkWidget *drawing; /* Loaded images. */ GdkPixbuf *image[MAX_IMAGES]; int n; /* Bounding box and velocity of each image. */ GdkRectangle area[MAX_IMAGES]; int u[MAX_IMAGES]; int v[MAX_IMAGES]; /* Rubberband state. */ gboolean rubber; int x1, y1; int x2, y2; GdkRectangle box; /* Bounding box of rubberband line */ } App; static void repaint_rect (App * app, GdkRectangle * rect) { gtk_widget_queue_draw_area (app->drawing, rect->x, rect->y, rect->width, rect->height); } static gboolean event_cb (GtkWidget * widget, GdkEvent * ev, App * app) { gboolean handled; handled = FALSE; switch (ev->type) { case GDK_BUTTON_PRESS: if (ev->button.button == 1) { app->rubber = TRUE; app->x1 = app->x2 = ev->button.x; app->y1 = app->y2 = ev->button.y; handled = TRUE; } break; case GDK_BUTTON_RELEASE: if (ev->button.button == 1) { app->rubber = FALSE; handled = TRUE; } break; case GDK_MOTION_NOTIFY: if (ev->motion.state & GDK_BUTTON1_MASK && app->rubber) { /* A hint? Read the position to get the latest value. */ if (ev->motion.is_hint) { int x, y; gdk_window_get_pointer (widget->window, &x, &y, NULL); ev->motion.x = x; ev->motion.y = y; } app->x2 = ev->motion.x; app->y2 = ev->motion.y; /* Queue a repaint at the old position to wipe out where te line * was. */ repaint_rect (app, &app->box); handled = TRUE; } break; default: break; } /* If we handled the event, update the bounding box for the rubberband * line and queue a repaint. */ if (handled) { app->box.x = MIN (app->x1, app->x2); app->box.width = MAX (app->x1, app->x2) - app->box.x; app->box.y = MIN (app->y1, app->y2); app->box.height = MAX (app->y1, app->y2) - app->box.y; repaint_rect (app, &app->box); } return handled; } static gboolean expose_cb (GtkDrawingArea * area, GdkEventExpose * event, App * app) { int i; for (i = 0; i < app->n; i++) { GdkRectangle repaint; if (gdk_rectangle_intersect (&event->area, &app->area[i], &repaint)) gdk_pixbuf_render_to_drawable (app->image[i], GTK_WIDGET (area)->window, GTK_WIDGET (area)->style->white_gc, repaint.x - app->area[i].x, repaint.y - app->area[i].y, repaint.x, repaint.y, repaint.width, repaint.height, GDK_RGB_DITHER_NORMAL, 0, 0); } if (app->rubber && gdk_rectangle_intersect (&event->area, &app->box, NULL)) gdk_draw_line (GTK_WIDGET (area)->window, GTK_WIDGET (area)->style->white_gc, app->x1, app->y1, app->x2, app->y2); return TRUE; } static gboolean timeout_cb (App * app) { int i; for (i = 0; i < app->n; i++) { const int right = app->drawing->allocation.width - app->area[i].width; const int bottom = app->drawing->allocation.height - app->area[i].height; int new_x, new_y; new_x = app->area[i].x + app->u[i]; new_y = app->area[i].y + app->v[i]; if (new_x < 0) { new_x = 0; app->u[i] *= -1; } if (new_x > right) { new_x = right; app->u[i] *= -1; } if (new_y < 0) { new_y = 0; app->v[i] *= -1; } if (new_y > bottom) { new_y = bottom; app->v[i] *= -1; } if (new_x != app->area[i].x || new_y != app->area[i].y) { repaint_rect (app, &app->area[i]); app->area[i].x = new_x; app->area[i].y = new_y; repaint_rect (app, &app->area[i]); } } return TRUE; } int main (int argc, char **argv) { App app; GtkWidget *win; GError *error = NULL; int i; gtk_init (&argc, &argv); for (i = 0; i < argc - 1 && i < MAX_IMAGES; i++) { if (!(app.image[i] = gdk_pixbuf_new_from_file (argv[i + 1], &error))) { fprintf (stderr, "%s\n", error->message); g_error_free (error); return -1; } app.area[i].x = random () % 100; app.area[i].y = random () % 100; app.area[i].width = gdk_pixbuf_get_width (app.image[i]); app.area[i].height = gdk_pixbuf_get_height (app.image[i]); app.u[i] = random () % 10 - 5; app.v[i] = random () % 10 - 5; } app.n = i; g_timeout_add (50, (GSourceFunc) timeout_cb, &app); win = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), NULL); app.drawing = gtk_drawing_area_new (); gtk_widget_add_events (GTK_WIDGET (app.drawing), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_signal_connect_after (GTK_OBJECT (app.drawing), "event", GTK_SIGNAL_FUNC (event_cb), &app); gtk_signal_connect (GTK_OBJECT (app.drawing), "expose_event", GTK_SIGNAL_FUNC (expose_cb), &app); gtk_container_add (GTK_CONTAINER (win), app.drawing); gtk_window_set_default_size (GTK_WINDOW (win), 250, 250); gtk_widget_show_all (win); gtk_main (); return 0; } ----------------------- John _______________________________________________ gtk-list mailing list gtk-list@xxxxxxxxx http://mail.gnome.org/mailman/listinfo/gtk-list