Hi all, We are stuck in a problem where we want to create an offscreen snapshot/pixbuf of a GtkWidget without showing the widget (realizing is allowed). We want the same pixels which are displayed on the screen when the widget is shown. Gdk does not do any drawing operations upon realize. It does so only when the widget is shown/exposed. We tried to send expose events to the realized widget but that also did not work. We have read that GtkWidgets are double-buffered and that all drawing operations go into the backend buffer, but we do not know if that can be used for solving our problem.
Pasted below are two programs. They can be compiled using 'g++ `pkg-config --cflags --libs gtk+-2.0` snapshot.cpp -lXpm' command line. Following is the program (this is a working one) which tries to get the offscreen snapshot of a shown widget: //////////////////////////////////////////////////////////////////////////////////////// #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <X11/Xlib.h> #include </usr/include/X11/xpm.h> GtkWidget *window; GtkWidget *button; GtkWidget *vbox; GtkWidget *drawing; static void hello( GtkWidget *widget, gpointer data ) { g_print ("Hello World\n"); gint width, height; GtkWidget *wid = widget; gtk_widget_show(wid); width = height = 200; GdkPixmap *gdkPixmap = gdk_pixmap_new(wid->window, width, height, -1); GdkGC *gc = gdk_gc_new(wid->window); gdk_draw_drawable(gdkPixmap, gc, wid->window, 0, 0, 0, 0, width, height); Pixmap xPixmap = GDK_PIXMAP_XID(gdkPixmap); XImage *mXimg = XGetImage(GDK_DISPLAY(), xPixmap, 0, 0, width, height, AllPlanes, ZPixmap); XpmWriteFileFromImage(GDK_DISPLAY(), "/tmp/snapshot.xpm", mXimg, 0, NULL); const gchar *l = gtk_button_get_label(GTK_BUTTON(wid)); g_print("%s\n", l); } static gboolean delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) { g_print ("delete event occurred\n"); return FALSE; } static void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } int main(int argc, char *argv[]) { gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); drawing = gtk_drawing_area_new(); vbox = gtk_vbox_new(TRUE, 0); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); button = gtk_button_new_with_label ("Hello World is good"); gtk_widget_set_size_request(button, 200, 200); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (hello), NULL); gtk_box_pack_start_defaults(GTK_BOX(vbox), button); gtk_box_pack_start_defaults(GTK_BOX(vbox), drawing); gtk_container_set_border_width(GTK_CONTAINER(window), 0); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_widget_show_all(window); gtk_main (); return 0; } //////////////////////////////////////////////////////////////////////////////////////// The above program creates a snapshot of 'button' in /tmp/snapshot.xpm upon clicking the button. But we want the same without showing the widget (say 'button' in the above program). Here's the program we have tried (it does not work): //////////////////////////////////////////////////////////////////////////////////////// #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <X11/Xlib.h> #include </usr/include/X11/xpm.h> GtkWidget *window; GtkWidget *button; GtkWidget *button1; GtkWidget *button2; GtkWidget *vbox; GtkWidget *drawing; static void hello( GtkWidget *widget, gpointer data ) { g_print ("Hello World\n"); gint width, height; GtkWidget *wid = widget; gtk_widget_show(wid); width = height = 200; GdkPixmap *gdkPixmap = gdk_pixmap_new(wid->window, width, height, -1); GdkGC *gc = gdk_gc_new(wid->window); gdk_draw_drawable(gdkPixmap, gc, wid->window, 0, 0, 0, 0, width, height); Pixmap xPixmap = GDK_PIXMAP_XID(gdkPixmap); XImage *mXimg = XGetImage(GDK_DISPLAY(), xPixmap, 0, 0, width, height, AllPlanes, ZPixmap); XpmWriteFileFromImage(GDK_DISPLAY(), "/tmp/snapshot.xpm", mXimg, 0, NULL); const gchar *l = gtk_button_get_label(GTK_BUTTON(wid)); g_print("%s\n", l); } static gboolean delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) { g_print ("delete event occurred\n"); return FALSE; } static void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } int main(int argc, char *argv[]) { gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); drawing = gtk_drawing_area_new(); vbox = gtk_vbox_new(TRUE, 0); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); button = gtk_button_new_with_label ("Hello World is good"); button1 = gtk_button_new_with_label ("Hello World 1"); button2 = gtk_button_new_with_label ("Hello World 2"); gtk_widget_set_size_request(button, 200, 200); gtk_widget_set_size_request(button1, 200, 200); gtk_widget_set_size_request(button2, 200, 200); g_signal_connect (G_OBJECT (button2), "clicked", G_CALLBACK (hello), NULL); gtk_box_pack_start_defaults(GTK_BOX(vbox), button); gtk_box_pack_start_defaults(GTK_BOX(vbox), button1); gtk_box_pack_start_defaults(GTK_BOX(vbox), button2); gtk_box_pack_start_defaults(GTK_BOX(vbox), drawing); gtk_container_set_border_width(GTK_CONTAINER(window), 0); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_widget_realize(window); gtk_widget_realize(vbox); gtk_widget_realize(button); gtk_widget_show(button); gtk_widget_show(vbox); //all this is just to send expose events to the GdkWindow programmatically. Since we want an offscreen snapshot of the widget without showing it, so we are trying to pass expose events so that it may draw itself correctly - but seems its not working //The hacks to send expose events start here GdkEvent eventExpose; eventExpose.expose.type = GDK_EXPOSE; eventExpose.expose.window = button->window; eventExpose.expose.send_event = TRUE; eventExpose.expose.area.x = 0; eventExpose.expose.area.y = 0; eventExpose.expose.area.width = 200; eventExpose.expose.area.height = 200; eventExpose.expose.count = 0; //gtk_widget_queue_draw(button); GdkRectangle rect; rect.x = 0; rect.y = 0; rect.width = 200; rect.height = 200; gdk_window_invalidate_rect(button->window, &rect, TRUE); gtk_widget_send_expose(button, &eventExpose); gtk_widget_send_expose(vbox, &eventExpose); //gdk_window_process_updates(button->window, TRUE); gdk_window_process_all_updates(); //The hacks to send expose events end here //Code to get the contents of the GdkWindow in a offscreen pixmap which we ultimately write to a file gint width, height; width = height = 200; GdkPixmap *gdkPixmap = gdk_pixmap_new(button->window, width, height, -1); GdkGC *gc = gdk_gc_new(button->window); gdk_draw_drawable(gdkPixmap, gc, button->window, 0, 0, 0, 0, width, height); Pixmap xPixmap = GDK_PIXMAP_XID(gdkPixmap); XImage *mXimg = XGetImage(GDK_DISPLAY(), xPixmap, 0, 0, width, height, AllPlanes, ZPixmap); XpmWriteFileFromImage(GDK_DISPLAY(), "/tmp/snapshot_new.xpm", mXimg, 0, NULL); // gtk_widget_realize(button); gtk_widget_realize(vbox); gtk_widget_realize(window); gtk_widget_show(button1); gtk_widget_show(button2); gtk_widget_show(drawing); gtk_widget_show(vbox); gtk_widget_show_all(window); //gtk_widget_hide(button); gtk_main (); return 0; } //////////////////////////////////////////////////////////////////////////////////////// Here we have hidden 'button' and try to send expose events and then get its contents offscreen but that always returns a garbage xpm in /tmp/snapshot_new.xpm Even the xpm which is created in the 'hello' callback (/tmp/snapshot.xpm) is not of the correct button in this case. Can someone provide any pointers for creating an offscreen snapshot of a GtkWidget. Thanks, narinder _______________________________________________ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-app-devel-list