Re: How to redraw the application window of a multithreaded GTK app?



Joerg Vogler wrote:
From: Olexiy Avramchenko [mailto:ath beast stu cn ua]
Sent: Wednesday, September 25, 2002 12:05 PM

3. Set the shared pixmap as the background of the drawing area window to
avoid handling "expose" event manually. There was a big thread "drawing
graphics" (09.19.2001) about.
Please skip this advice, it's not for your situation. Some lines from "Xlib.PS":
"If you later draw into the pixmap used for the background, what happens
is undefined because the X implementation is free to make a copy of the pixmap
or to use the same pixmap."

So, you have to implement a little "expose" handler.

Take a look at the demo I've attached. It's brutal mt framer - it draws frames
to the screen as fast as it can do.

Olexiy

/*
	It's a gtk+-1.2 program, so you can compile it with:
	gcc -O2 -Wall `gtk-config --cflags --libs gthread` framer.c -o framer

	This app is an example only, bugs may be found.

	Olexiy
*/

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include <gtk/gtk.h>


static GtkWidget *area=NULL;

static GdkPixmap *back_buffer=NULL;

static gboolean  threadling_must_die=FALSE;
static pthread_t threadling_tid=0;


/*
	this's a thread function - it fills back_buffer pixmap and
	draws it into the screen. it's really bad, cause it will eat
	almost all CPU time.
*/
static void *wild_threadling(void *arg)
{
int		z=0, dz=1;
char		fps[128];
gulong		us, us_backup=0;
double		frames_count=0;
struct timeval	tv0,tv1;

	strcpy(fps, "frame: ?x?, fps: ?");
	gettimeofday(&tv0, NULL);
	while (1) {
		/* checking whether we have to quit */
		if (threadling_must_die)
			break;	/* use pthread_exit() instead, if you want */

		gdk_threads_enter();

		/* clearing frame */
		gdk_draw_rectangle(
					back_buffer,
					area->style->black_gc,
					TRUE,
					0,0, -1,-1
		);

		/* recalculating rect coordinates */
		z += dz;
		if (z >= area->allocation.width/2 || z >= area->allocation.height/2) {
			z = area->allocation.width<area->allocation.height?
				area->allocation.width/2:area->allocation.height/2;
			dz *= -1;
		}
		if (z <= 0) {
			z = 0;
			dz *= -1;
		}

		/* draw white rect inside */
		gdk_draw_rectangle(
					back_buffer,
					area->style->white_gc,
					FALSE,
					z,z,
					area->allocation.width-z*2,area->allocation.height-z*2
		);

		/* calculating fps, setting up info string */
		gettimeofday(&tv1, NULL);
		us = (tv1.tv_sec-tv0.tv_sec)*1000000 + tv1.tv_usec-tv0.tv_usec;
		if (us-us_backup >= 1000000) {
			sprintf(
				fps,
				"frame: %dx%d, fps: %.2f",
				area->allocation.width, area->allocation.height,
				frames_count*1000000/(us-us_backup)
			);
			us_backup = us;
			frames_count = 0;
		}

		/* drawing info string to pixmap */
		gdk_draw_string(
				back_buffer,
				area->style->font,
				area->style->white_gc,
				8, 8+gdk_string_height(area->style->font, fps),
				fps
		);

		/* drawing pixmap to screen */
		gdk_draw_pixmap(
				area->window,
				area->style->white_gc,
				back_buffer,
				0,0, 0,0, -1,-1
		);

		/* syncing with X server */
		gdk_flush();

		gdk_threads_leave();

		frames_count++;
	}

	return NULL;
}

/*
	yes, we're implementing an expose event handler, cause i found
	this in X lib PS file:
	"If you later draw into the pixmap used for the background, what
	happens is undefined because the X implementation is free to make
	a copy of the pixmap or to use the same pixmap."
*/
static gboolean area_expose(GtkWidget *area, GdkEventExpose *event)
{
	gdk_draw_pixmap(
			area->window,
			area->style->white_gc,
			back_buffer,
			event->area.x, event->area.y,
			event->area.x, event->area.y,
			event->area.width, event->area.height
	);
	return TRUE;
}

static gboolean area_configure(GtkWidget *area, GdkEventConfigure *event)
{
	/* if pixmap was already allocated - free it */
	if (back_buffer)
		gdk_pixmap_unref(back_buffer);

	/* allocating back pixmap */
	back_buffer = gdk_pixmap_new(
					area->window,
					area->allocation.width,
					area->allocation.height,
					-1
	);

	if (!back_buffer)
		g_error(
			"gdk_pixmap_new(%p, %d,%d, -1) failed.\n",
			area,
			area->allocation.width,area->allocation.height
		);

	/* filling all pixmap with white color */
	gdk_draw_rectangle(
				back_buffer,
				area->style->white_gc,
				TRUE,
				0,0, -1,-1
	);

	return TRUE;
}

void thread_launcher(GtkToggleButton *button)
{
	if (button->active) {
	/* "run thread" checker button pressed - launching thread */
		pthread_create(
				&threadling_tid,
				NULL,
				wild_threadling,
				NULL
		);
	}
	else	if (threadling_tid) {
	/* "run thread" checker button released and thread is running -
	    killing thread */
			threadling_must_die = TRUE;
			pthread_join(threadling_tid, NULL);
			threadling_must_die = FALSE;
			threadling_tid = 0;
		}
}

int main(int argc, char **argv)
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *button;

	g_thread_init(NULL);

	gtk_init(&argc, &argv);

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_container_set_border_width((GtkContainer*)window, 4);
	gtk_signal_connect(
				(GtkObject*)window,
				"delete_event",
				(GtkSignalFunc)gtk_main_quit,
				NULL
	);

	vbox = gtk_vbox_new(FALSE, 4);
	gtk_container_add((GtkContainer*)window, vbox);

	area = gtk_drawing_area_new();
	gtk_signal_connect(
				(GtkObject*)area,
				"expose_event",
				(GtkSignalFunc)area_expose,
				NULL
	);
	gtk_signal_connect(
				(GtkObject*)area,
				"configure_event",
				(GtkSignalFunc)area_configure,
				NULL
	);
	gtk_drawing_area_size((GtkDrawingArea*)area, 256,256);
	gtk_box_pack_start((GtkBox*)vbox, area, TRUE,TRUE, 0);

	button = gtk_check_button_new_with_label("run thread");
	gtk_signal_connect(
				(GtkObject*)button,
				"toggled",
				(GtkSignalFunc)thread_launcher,
				NULL
	);
	gtk_box_pack_start((GtkBox*)vbox, button, FALSE,FALSE, 0);

	button = gtk_button_new_with_label("quit");
	gtk_box_pack_start((GtkBox*)vbox, button, FALSE,FALSE, 0);
	gtk_signal_connect(
				(GtkObject*)button,
				"clicked",
				(GtkSignalFunc)gtk_main_quit,
				NULL
	);

	gtk_widget_show_all(window);

	gdk_threads_enter();
	gtk_main();
	gdk_threads_leave();

	return 0;
}


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