Re: How to redraw the application window of a multithreaded GTK app?
- From: Olexiy Avramchenko <ath beast stu cn ua>
- To: Joerg Vogler <vogler panasonic de>
- Cc: GTK Mailing List <gtk-list gnome org>
- Subject: Re: How to redraw the application window of a multithreaded GTK app?
- Date: Thu, 26 Sep 2002 14:41:46 +0300
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]