Re: GDK_POINTER_MOTION_HINT_MASK has no effect
- From: jcupitt gmail com
- To: stewart weiss acm org
- Cc: gtk-list <gtk-list gnome org>
- Subject: Re: GDK_POINTER_MOTION_HINT_MASK has no effect
- Date: Sun, 2 Dec 2007 21:58:08 +0000
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]