Re: GDK_POINTER_MOTION_HINT_MASK has no effect



Hi again Stewart,

On Dec 2, 2007 5:12 AM, Stewart Weiss <stewart weiss acm org> 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]