summaryrefslogtreecommitdiff
path: root/perf
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@ximian.com>2005-07-29 00:38:51 +0000
committerFederico Mena Quintero <federico@src.gnome.org>2005-07-29 00:38:51 +0000
commitb4f52020f0fd86ddda88cdded1e4e84b47f384b6 (patch)
tree1b22478a6f40c3351474d546fb617f72b81418ec /perf
parent375cb32bc107b1e69be49689fd096b1c961d94af (diff)
downloadgtk+-b4f52020f0fd86ddda88cdded1e4e84b47f384b6.tar.gz
Update for the new API of the profiler.
2005-07-28 Federico Mena Quintero <federico@ximian.com> * perf/README: Update for the new API of the profiler. * perf/gtkwidgetprofiler.[ch]: New files with a widget profiler object. This is the old content of timers.[ch] turned into a nice object, with signals for creation and reporting. The profiler needs to maintain some state when reusing the widget, so it's useful to turn it into a real object. Break down timing show_all into GTK_WIDGET_PROFILER_REPORT_MAP and GTK_WIDGET_PROFILER_REPORT_EXPOSE. * perf/main.c: Refactor to use GtkWidgetProfiler. * perf/appwindow.c (content_area_new): Make this just create a notebook, instead of a complex arrangement of panes. * perf/widgets.h: New header file for all the "create a widget" utility functions. * perf/treeview.c: New file. Moved the tree view part from appwindow.c over to here; GtkTreeView really needs its own tests. (tree_view_new): Set the shadow type to IN. * perf/textview.c: Likewise moved over from appwindow.c, but for GtkTextView. (text_view_new): Set the shadow type to IN. * perf/Makefile.am (testperf_SOURCES): Add the new source files; remove appwindow.h and timers.[ch]. * perf/timers.[ch]: Removed. * perf/appwindow.h: Removed.
Diffstat (limited to 'perf')
-rw-r--r--perf/Makefile.am79
-rw-r--r--perf/README123
-rw-r--r--perf/appwindow.c214
-rw-r--r--perf/appwindow.h3
-rw-r--r--perf/gtkwidgetprofiler.c377
-rw-r--r--perf/gtkwidgetprofiler.h58
-rw-r--r--perf/main.c36
-rw-r--r--perf/marshalers.list2
-rw-r--r--perf/textview.c60
-rw-r--r--perf/timers.c134
-rw-r--r--perf/timers.h16
-rw-r--r--perf/treeview.c101
-rw-r--r--perf/widgets.h7
13 files changed, 787 insertions, 423 deletions
diff --git a/perf/Makefile.am b/perf/Makefile.am
index 8e7ae18611..046e544197 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -28,12 +28,73 @@ testperf_DEPENDENCIES = $(TEST_DEPS)
testperf_LDADD = $(LDADDS)
-testperf_SOURCES = \
- appwindow.c \
- appwindow.h \
- main.c \
- timers.c \
- timers.h
-
-EXTRA_DIST = \
- README
+testperf_SOURCES = \
+ appwindow.c \
+ gtkwidgetprofiler.c \
+ gtkwidgetprofiler.h \
+ main.c \
+ marshalers.c \
+ marshalers.h \
+ textview.c \
+ treeview.c \
+ typebuiltins.c \
+ typebuiltins.h \
+ widgets.h
+
+BUILT_SOURCES = \
+ marshalers.c \
+ marshalers.h \
+ typebuiltins.c \
+ typebuiltins.h
+
+stamp_files = \
+ stamp-marshalers.h \
+ stamp-typebuiltins.h
+
+headers_with_enums = \
+ gtkwidgetprofiler.h
+
+MAINTAINERCLEANFILES = $(BUILT_SOURCES) $(stamp_files)
+
+marshalers.h: stamp-marshalers.h
+ @true
+
+stamp-marshalers.h: @REBUILD@ marshalers.list
+ $(GLIB_GENMARSHAL) --prefix=_gtk_marshal $(srcdir)/marshalers.list --header >> xgen-gmlh \
+ && (cmp -s xgen-gmlh marshalers.h || cp xgen-gmlh marshalers.h) \
+ && rm -f xgen-gmlh \
+ && echo timestamp > $(@F)
+marshalers.c: @REBUILD@ marshalers.list
+ $(GLIB_GENMARSHAL) --prefix=_gtk_marshal $(srcdir)/marshalers.list --body >> xgen-gmlc \
+ && cp xgen-gmlc marshalers.c \
+ && rm -f xgen-gmlc
+
+typebuiltins.h: stamp-typebuiltins.h
+ @true
+stamp-typebuiltins.h: @REBUILD@ $(headers_with_enums) Makefile
+ ( cd $(srcdir) && glib-mkenums \
+ --fhead "#ifndef __TYPE_BUILTINS_H__\n#define __TYPE_BUILTINS_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define GTK_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* __TYPE_BUILTINS_H__ */" \
+ $(headers_with_enums) ) >> xgen-gtbh \
+ && (cmp -s xgen-gtbh typebuiltins.h || cp xgen-gtbh typebuiltins.h ) \
+ && rm -f xgen-gtbh \
+ && echo timestamp > $(@F)
+typebuiltins.c: @REBUILD@ $(headers_with_enums) Makefile
+ ( cd $(srcdir) && glib-mkenums \
+ --fhead "#include \"gtkwidgetprofiler.h\"" \
+ --ftail "#define __TYPE_BUILTINS_C__\n" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
+ $(headers_with_enums) ) > xgen-gtbc \
+ && cp xgen-gtbc typebuiltins.c \
+ && rm -f xgen-gtbc
+
+
+EXTRA_DIST = \
+ README \
+ marshalers.list \
+ $(BUILT_SOURCES)
diff --git a/perf/README b/perf/README
index 43aa9aa351..7f2670d546 100644
--- a/perf/README
+++ b/perf/README
@@ -6,63 +6,75 @@ performant does not only mean "paint widgets fast". It also means
things like the time needed to set up widgets, to map and draw a
window for the first time, and emitting/propagating signals.
-The following is accurate as of 2005/07/26.
+The following is accurate as of 2005/07/28.
Using the framework
-------------------
Right now the framework is very simple; it just has utility functions
-to time widget creation, drawing, and destruction. To run such a
-test, you use the functions in timers.h. You can call this:
+to time widget creation, mapping, exposure, and destruction. To run
+such a test, you use the GtkWidgetProfiler object in
+gtkwidgetprofiler.h.
- timer_time_widget (create_func, report_func, user_data);
+The gtk_widget_profiler_profile_boot() function will emit the
+"create-widget" signal so that you can create your widget for
+testing. It will then take timings for the widget, and emit the
+"report" signal as appropriate.
-You must provide the create_funcn and report_func callbacks.
+The "create-widget" signal:
-The create_func:
+ The handler has this form:
- This simply creates a toplevel window with some widgets inside it.
- It is important that you do not show any of the widgets; the
- framework will call gtk_widget_show_all() on the toplevel window
- automatically at the right time.
+ GtkWidget *create_widget_callback (GtkWidgetProfiler *profiler,
+ gpointer user_data);
-The report_func:
+ You need to create a widget in your handler, and return it. Do not
+ show the widget; the profiler will do that by itself at the right
+ time, and will actually complain if you show the widget.
- This function will get called when timer_time_widget() reaches an
- interesting point in the lifecycle of your widget. See timers.h and
- the TimerReport enumeration; this is what gets passed as the
- "report" argument to your report_func. Right now, your function
- will be called three times for each call to timer_time_widget():
- 1. With report = TIMER_REPORT_WIDGET_CREATION. A timer gets
- started right before timer_time_widget() calls create_func,
- and it gets stopped when your create_func returns. This
- measures the time it takes to set up a toplevel window (but
- not show it).
+The "report" signal:
- 2. With report = TIMER_REPORT_WIDGET_SHOW. A timer gets started
- right before timer_time_widget() calls gtk_widget_show_all()
- on your toplevel window, and it gets stopped when the window
- has been fully shown and painted to the screen.
+ This function will get called when the profiler wants to report that
+ it finished timing an important stage in the lifecycle of your
+ widget. The handler has this form:
- 3. With report = TIMER_REPORT_WIDGET_DESTRUCTION. A timer gets
- started right before timer_time_widget() calls
- gtk_widget_destroy() on your toplevel window, and it gets
- stopped when gtk_widget_destroy() returns.
+ void report_callback (GtkWidgetProfiler *profiler,
+ GtkWidgetProfilerReport report,
+ GtkWidget *widget,
+ gdouble elapsed,
+ gpointer user_data);
-As a very basic example of using timer_time_widget(), you can use
-something like this:
+ The "report" argument tells you what happened to your widget:
+
+ GTK_WIDGET_PROFILER_REPORT_CREATE. A timer gets started right
+ before the profiler emits the "create-widget" signal,, and it gets
+ stopped when your callback returns with the new widget. This
+ measures the time it takes to set up your widget, but not show it.
+
+ GTK_WIDGET_PROFILER_REPORT_MAP. A timer gets started right before
+ the profiler calls gtk_widget_show_all() on your widget, and it
+ gets stopped when the the widget has been mapped.
+
+ GTK_WIDGET_PROFILER_REPORT_EXPOSE. A timer gets started right before
+ the profiler starts waiting for GTK+ and the X server to finish
+ painting your widget, and it gets stopped when the widget is fully
+ painted to the screen.
+
+ GTK_WIDGET_PROFILER_REPORT_DESTROY. A timer gets started right
+ before the profiler calls gtk_widget_destroy() on your widget, and
+ it gets stopped when gtk_widget_destroy() returns.
+
+As a very basic example of using GtkWidgetProfiler is this:
----------------------------------------------------------------------
#include <stdio.h>
#include <gtk/gtk.h>
-#include "timers.h"
-
-#define ITERS 20
+#include "gtkwidgetprofiler.h"
static GtkWidget *
-create_cb (gpointer data)
+create_widget_cb (GtkWidgetProfiler *profiler, gpointer data)
{
GtkWidget *window;
@@ -73,47 +85,66 @@ create_cb (gpointer data)
}
static void
-report_cb (TimerReport report, gdouble elapsed, gpointer data)
+report_cb (GtkWidgetProfiler *profiler, GtkWidgetProfilerReport report, GtkWidget *widget, gdouble elapsed, gpointer data)
{
const char *type;
switch (report) {
- case TIMER_REPORT_WIDGET_CREATION:
+ case GTK_WIDGET_PROFILER_REPORT_CREATE:
type = "widget creation";
break;
- case TIMER_REPORT_WIDGET_SHOW:
- type = "widget show";
+ case GTK_WIDGET_PROFILER_REPORT_MAP:
+ type = "widget map";
break;
- case TIMER_REPORT_WIDGET_DESTRUCTION:
+ case GTK_WIDGET_PROFILER_REPORT_EXPOSE:
+ type = "widget expose";
+ break;
+
+ case GTK_WIDGET_PROFILER_REPORT_DESTROY:
type = "widget destruction";
break;
+
+ default:
+ g_assert_not_reached ();
+ type = NULL;
}
fprintf (stderr, "%s: %g sec\n", type, elapsed);
+
+ if (report == GTK_WIDGET_PROFILER_REPORT_DESTROY)
+ fputs ("\n", stderr);
}
int
main (int argc, char **argv)
{
- int i;
+ GtkWidgetProfiler *profiler;
gtk_init (&argc, &argv);
- for (i = 0; i < ITERS; i++)
- timer_time_widget (create_cb, report_cb, NULL);
+ profiler = gtk_widget_profiler_new ();
+ g_signal_connect (profiler, "create-widget",
+ G_CALLBACK (create_widget_cb), NULL);
+ g_signal_connect (profiler, "report",
+ G_CALLBACK (report_cb), NULL);
+
+ gtk_widget_profiler_set_num_iterations (profiler, 100);
+ gtk_widget_profiler_profile_boot (profiler);
return 0;
-}
+}
+
----------------------------------------------------------------------
Getting meaningful results
--------------------------
-Getting times for widget creation/drawing/destruction is interesting,
-but how do you actually find the places that need optimizing?
+Getting times for widget creation/mapping/exposing/destruction is
+interesting, but how do you actually find the places that need
+optimizing?
Why, you run the tests under a profiler, of course.
diff --git a/perf/appwindow.c b/perf/appwindow.c
index 6230c91dc2..cb239abc94 100644
--- a/perf/appwindow.c
+++ b/perf/appwindow.c
@@ -13,6 +13,8 @@
#include <string.h>
#include <gtk/gtk.h>
+#include "widgets.h"
+
static void
quit_cb (GtkWidget *widget, gpointer data)
{
@@ -150,231 +152,39 @@ toolbar_new (GtkUIManager *ui)
return gtk_ui_manager_get_widget (ui, "/MainToolbar");
}
-struct row_data {
- char *stock_id;
- char *text1;
- char *text2;
-};
-
-static struct row_data row_data[] = {
- { GTK_STOCK_NEW, "First", "Here bygynneth the Book of the tales of Caunterbury." },
- { GTK_STOCK_OPEN, "Second", "Whan that Aprille, with hise shoures soote," },
- { GTK_STOCK_ABOUT, "Third", "The droghte of March hath perced to the roote" },
- { GTK_STOCK_ADD, "Fourth", "And bathed every veyne in swich licour," },
- { GTK_STOCK_APPLY, "Fifth", "Of which vertu engendred is the flour;" },
- { GTK_STOCK_BOLD, "Sixth", "Whan Zephirus eek with his swete breeth" },
- { GTK_STOCK_CANCEL, "Seventh", "Inspired hath in every holt and heeth" },
- { GTK_STOCK_CDROM, "Eighth", "The tendre croppes, and the yonge sonne" },
- { GTK_STOCK_CLEAR, "Ninth", "Hath in the Ram his halfe cours yronne," },
- { GTK_STOCK_CLOSE, "Tenth", "And smale foweles maken melodye," },
- { GTK_STOCK_COLOR_PICKER, "Eleventh", "That slepen al the nyght with open eye-" },
- { GTK_STOCK_CONVERT, "Twelfth", "So priketh hem Nature in hir corages-" },
- { GTK_STOCK_CONNECT, "Thirteenth", "Thanne longen folk to goon on pilgrimages" },
- { GTK_STOCK_COPY, "Fourteenth", "And palmeres for to seken straunge strondes" },
- { GTK_STOCK_CUT, "Fifteenth", "To ferne halwes, kowthe in sondry londes;" },
- { GTK_STOCK_DELETE, "Sixteenth", "And specially, from every shires ende" },
- { GTK_STOCK_DIRECTORY, "Seventeenth", "Of Engelond, to Caunturbury they wende," },
- { GTK_STOCK_DISCONNECT, "Eighteenth", "The hooly blisful martir for the seke" },
- { GTK_STOCK_EDIT, "Nineteenth", "That hem hath holpen, whan that they were seeke." },
- { GTK_STOCK_EXECUTE, "Twentieth", "Bifil that in that seson, on a day," },
- { GTK_STOCK_FILE, "Twenty-first", "In Southwerk at the Tabard as I lay," },
- { GTK_STOCK_FIND, "Twenty-second", "Redy to wenden on my pilgrymage" },
- { GTK_STOCK_FIND_AND_REPLACE, "Twenty-third", "To Caunterbury, with ful devout corage," },
- { GTK_STOCK_FLOPPY, "Twenty-fourth", "At nyght were come into that hostelrye" },
- { GTK_STOCK_FULLSCREEN, "Twenty-fifth", "Wel nyne and twenty in a compaignye" },
- { GTK_STOCK_GOTO_BOTTOM, "Twenty-sixth", "Of sondry folk, by aventure yfalle" },
-};
-
-static GtkTreeModel *
-tree_model_new (void)
-{
- GtkListStore *list;
- int i;
-
- list = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
-
- for (i = 0; i < G_N_ELEMENTS (row_data); i++)
- {
- GtkTreeIter iter;
-
- gtk_list_store_append (list, &iter);
- gtk_list_store_set (list,
- &iter,
- 0, row_data[i].stock_id,
- 1, row_data[i].text1,
- 2, row_data[i].text2,
- -1);
- }
-
- return GTK_TREE_MODEL (list);
-}
-
static GtkWidget *
-tree_view_new (void)
+drawing_area_new (void)
{
- GtkWidget *sw;
- GtkWidget *tree;
- GtkTreeModel *model;
- GtkTreeViewColumn *column;
-
- sw = gtk_scrolled_window_new (NULL, NULL);
-
- model = tree_model_new ();
- tree = gtk_tree_view_new_with_model (model);
- g_object_unref (model);
-
- gtk_widget_set_size_request (tree, 300, 100);
-
- column = gtk_tree_view_column_new_with_attributes ("Icon",
- gtk_cell_renderer_pixbuf_new (),
- "stock-id", 0,
- NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
-
- column = gtk_tree_view_column_new_with_attributes ("Index",
- gtk_cell_renderer_text_new (),
- "text", 1,
- NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
-
- column = gtk_tree_view_column_new_with_attributes ("Canterbury Tales",
- gtk_cell_renderer_text_new (),
- "text", 2,
- NULL);
- gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
-
- gtk_container_add (GTK_CONTAINER (sw), tree);
-
- return sw;
-}
-
-static GtkWidget *
-left_pane_new (void)
-{
- return tree_view_new ();
-}
+ GtkWidget *darea;
-static GtkWidget *
-text_view_new (void)
-{
- GtkWidget *sw;
- GtkWidget *text_view;
- GtkTextBuffer *buffer;
-
- sw = gtk_scrolled_window_new (NULL, NULL);
-
- text_view = gtk_text_view_new ();
- gtk_widget_set_size_request (text_view, 400, 300);
- gtk_container_add (GTK_CONTAINER (sw), text_view);
-
- buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
-
- gtk_text_buffer_set_text (buffer,
- "In felaweshipe, and pilgrimes were they alle,\n"
- "That toward Caunterbury wolden ryde.\n"
- "The chambres and the stables weren wyde,\n"
- "And wel we weren esed atte beste;\n"
- "And shortly, whan the sonne was to reste,\n"
- "\n"
- "So hadde I spoken with hem everychon \n"
- "That I was of hir felaweshipe anon, \n"
- "And made forward erly for to ryse \n"
- "To take our wey, ther as I yow devyse. \n"
- " But nathelees, whil I have tyme and space, \n"
- " \n"
- "Er that I ferther in this tale pace, \n"
- "Me thynketh it acordaunt to resoun \n"
- "To telle yow al the condicioun \n"
- "Of ech of hem, so as it semed me, \n"
- "And whiche they weren, and of what degree, \n"
- " \n"
- "And eek in what array that they were inne; \n"
- "And at a knyght than wol I first bigynne. \n"
- " A knyght ther was, and that a worthy man, \n"
- "That fro the tyme that he first bigan \n"
- "To riden out, he loved chivalrie, \n"
- " \n"
- "Trouthe and honour, fredom and curteisie. \n"
- "Ful worthy was he in his lordes werre, \n"
- " \n"
- "And therto hadde he riden, no man ferre, \n"
- "As wel in Cristendom as in Hethenesse, \n"
- "And evere honoured for his worthynesse. \n"
- " \n"
- " At Alisaundre he was, whan it was wonne; \n"
- "Ful ofte tyme he hadde the bord bigonne \n"
- "Aboven alle nacions in Pruce; \n"
- "In Lettow hadde he reysed, and in Ruce, \n"
- "No cristen man so ofte of his degree. \n",
- -1);
-
- return sw;
+ darea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (darea, 640, 480);
+ return darea;
}
static GtkWidget *
-upper_right_new (void)
+content_area_new (void)
{
GtkWidget *notebook;
notebook = gtk_notebook_new ();
gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
- text_view_new (),
+ drawing_area_new (),
gtk_label_new ("First"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
- text_view_new (),
+ drawing_area_new (),
gtk_label_new ("Second"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
- text_view_new (),
+ drawing_area_new (),
gtk_label_new ("Third"));
return notebook;
}
static GtkWidget *
-lower_right_new (void)
-{
- return tree_view_new ();
-}
-
-static GtkWidget *
-right_pane_new (void)
-{
- GtkWidget *paned;
- GtkWidget *upper_right;
- GtkWidget *lower_right;
-
- paned = gtk_vpaned_new ();
-
- upper_right = upper_right_new ();
- gtk_paned_pack1 (GTK_PANED (paned), upper_right, TRUE, TRUE);
-
- lower_right = lower_right_new ();
- gtk_paned_pack2 (GTK_PANED (paned), lower_right, TRUE, TRUE);
-
- return paned;
-}
-
-static GtkWidget *
-content_area_new (void)
-{
- GtkWidget *hpaned;
- GtkWidget *left, *right;
-
- hpaned = gtk_hpaned_new ();
-
- left = left_pane_new ();
- gtk_paned_pack1 (GTK_PANED (hpaned), left, TRUE, TRUE);
-
- right = right_pane_new ();
- gtk_paned_pack2 (GTK_PANED (hpaned), right, TRUE, TRUE);
-
- return hpaned;
-}
-
-static GtkWidget *
status_bar_new (void)
{
return gtk_statusbar_new ();
@@ -417,7 +227,7 @@ appwindow_new (void)
gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
widget = status_bar_new ();
- gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
return window;
}
diff --git a/perf/appwindow.h b/perf/appwindow.h
deleted file mode 100644
index 1975fe02f2..0000000000
--- a/perf/appwindow.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#include <gtk/gtkwidget.h>
-
-GtkWidget *appwindow_new (void);
diff --git a/perf/gtkwidgetprofiler.c b/perf/gtkwidgetprofiler.c
new file mode 100644
index 0000000000..01ae8420ed
--- /dev/null
+++ b/perf/gtkwidgetprofiler.c
@@ -0,0 +1,377 @@
+#include "config.h"
+#include <string.h>
+#include "gtkwidgetprofiler.h"
+#include "marshalers.h"
+#include "typebuiltins.h"
+
+typedef enum {
+ STATE_NOT_CREATED,
+ STATE_INSTRUMENTED_NOT_MAPPED,
+ STATE_INSTRUMENTED_MAPPED
+} State;
+
+struct _GtkWidgetProfilerPrivate {
+ State state;
+
+ GtkWidget *profiled_widget;
+ GtkWidget *toplevel;
+
+ int n_iterations;
+
+ GTimer *timer;
+
+ gulong toplevel_expose_event_id;
+ gulong toplevel_property_notify_event_id;
+
+ GdkAtom profiler_atom;
+
+ guint profiling : 1;
+};
+
+G_DEFINE_TYPE (GtkWidgetProfiler, gtk_widget_profiler, G_TYPE_OBJECT);
+
+static void gtk_widget_profiler_finalize (GObject *object);
+
+enum {
+ CREATE_WIDGET,
+ REPORT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+gtk_widget_profiler_class_init (GtkWidgetProfilerClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ signals[CREATE_WIDGET] =
+ g_signal_new ("create-widget",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkWidgetProfilerClass, create_widget),
+ NULL, NULL,
+ _gtk_marshal_OBJECT__VOID,
+ G_TYPE_OBJECT, 0);
+
+ signals[REPORT] =
+ g_signal_new ("report",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkWidgetProfilerClass, report),
+ NULL, NULL,
+ _gtk_marshal_VOID__ENUM_OBJECT_DOUBLE,
+ G_TYPE_NONE, 3,
+ GTK_TYPE_WIDGET_PROFILER_REPORT,
+ G_TYPE_OBJECT,
+ G_TYPE_DOUBLE);
+
+ object_class->finalize = gtk_widget_profiler_finalize;
+}
+
+static void
+gtk_widget_profiler_init (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+
+ priv = g_new0 (GtkWidgetProfilerPrivate, 1);
+ profiler->priv = priv;
+
+ priv->state = STATE_NOT_CREATED;
+ priv->n_iterations = 1;
+
+ priv->timer = g_timer_new ();
+
+ priv->profiler_atom = gdk_atom_intern ("GtkWidgetProfiler", FALSE);
+}
+
+static void
+reset_state (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+
+ priv = profiler->priv;
+
+ if (priv->toplevel)
+ {
+ g_signal_handler_disconnect (priv->toplevel, priv->toplevel_expose_event_id);
+ priv->toplevel_expose_event_id = 0;
+
+ g_signal_handler_disconnect (priv->toplevel, priv->toplevel_property_notify_event_id);
+ priv->toplevel_property_notify_event_id = 0;
+
+ gtk_widget_destroy (priv->toplevel);
+ priv->toplevel = NULL;
+ priv->profiled_widget = NULL;
+ }
+
+ priv->state = STATE_NOT_CREATED;
+}
+
+static void
+gtk_widget_profiler_finalize (GObject *object)
+{
+ GtkWidgetProfiler *profiler;
+ GtkWidgetProfilerPrivate *priv;
+
+ profiler = GTK_WIDGET_PROFILER (object);
+ priv = profiler->priv;
+
+ reset_state (profiler);
+ g_timer_destroy (priv->timer);
+
+ g_free (priv);
+
+ G_OBJECT_CLASS (gtk_widget_profiler_parent_class)->finalize (object);
+}
+
+GtkWidgetProfiler *
+gtk_widget_profiler_new (void)
+{
+ return g_object_new (GTK_TYPE_WIDGET_PROFILER, NULL);
+}
+
+void
+gtk_widget_profiler_set_num_iterations (GtkWidgetProfiler *profiler,
+ gint n_iterations)
+{
+ GtkWidgetProfilerPrivate *priv;
+
+ g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
+ g_return_if_fail (n_iterations > 0);
+
+ priv = profiler->priv;
+ priv->n_iterations = n_iterations;
+}
+
+static void
+report (GtkWidgetProfiler *profiler,
+ GtkWidgetProfilerReport report,
+ gdouble elapsed)
+{
+ GtkWidgetProfilerPrivate *priv;
+
+ priv = profiler->priv;
+
+ g_signal_emit (profiler, signals[REPORT], 0, report, priv->profiled_widget, elapsed);
+}
+
+static GtkWidget *
+create_widget_via_emission (GtkWidgetProfiler *profiler)
+{
+ GtkWidget *widget;
+
+ widget = NULL;
+ g_signal_emit (profiler, signals[CREATE_WIDGET], 0, &widget);
+ if (!widget)
+ g_error ("The profiler emitted the \"create-widget\" signal but the signal handler returned no widget!");
+
+ if (GTK_WIDGET_VISIBLE (widget) || GTK_WIDGET_MAPPED (widget))
+ g_error ("The handler for \"create-widget\" must return an unmapped and unshown widget");
+
+ return widget;
+}
+
+static gboolean
+toplevel_property_notify_event_cb (GtkWidget *widget, GdkEventProperty *event, gpointer data)
+{
+ GtkWidgetProfiler *profiler;
+ GtkWidgetProfilerPrivate *priv;
+ gdouble elapsed;
+
+ profiler = GTK_WIDGET_PROFILER (data);
+ priv = profiler->priv;
+
+ if (event->atom != priv->profiler_atom)
+ return FALSE;
+
+ /* Finish timing map/expose */
+
+ elapsed = g_timer_elapsed (priv->timer, NULL);
+ report (profiler, GTK_WIDGET_PROFILER_REPORT_EXPOSE, elapsed);
+
+ gtk_main_quit (); /* This will get us back to the end of profile_map_expose() */
+ return TRUE;
+}
+
+static gboolean
+toplevel_idle_after_expose_cb (gpointer data)
+{
+ GtkWidgetProfiler *profiler;
+ GtkWidgetProfilerPrivate *priv;
+
+ profiler = GTK_WIDGET_PROFILER (data);
+ priv = profiler->priv;
+
+ gdk_property_change (priv->toplevel->window,
+ priv->profiler_atom,
+ gdk_atom_intern ("STRING", FALSE),
+ 8,
+ GDK_PROP_MODE_REPLACE,
+ "hello",
+ strlen ("hello"));
+
+ return FALSE;
+}
+
+static gboolean
+toplevel_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ GtkWidgetProfiler *profiler;
+
+ profiler = GTK_WIDGET_PROFILER (data);
+
+ g_idle_add_full (G_PRIORITY_HIGH, toplevel_idle_after_expose_cb, profiler, NULL);
+ return FALSE;
+}
+
+static void
+instrument_toplevel (GtkWidgetProfiler *profiler,
+ GtkWidget *toplevel)
+{
+ GtkWidgetProfilerPrivate *priv;
+
+ priv = profiler->priv;
+
+ priv->toplevel_expose_event_id = g_signal_connect (toplevel, "expose-event",
+ G_CALLBACK (toplevel_expose_event_cb), profiler);
+
+ gtk_widget_add_events (toplevel, GDK_PROPERTY_CHANGE_MASK);
+ priv->toplevel_property_notify_event_id = g_signal_connect (toplevel, "property-notify-event",
+ G_CALLBACK (toplevel_property_notify_event_cb), profiler);
+}
+
+static GtkWidget *
+ensure_and_get_toplevel (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
+ GtkWidget *window;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (GTK_WIDGET_TOPLEVEL (toplevel))
+ return toplevel;
+
+ g_assert (toplevel == widget); /* we don't want extraneous ancestors */
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_container_add (GTK_CONTAINER (window), widget);
+
+ return window;
+}
+
+static GtkWidget *
+get_instrumented_toplevel (GtkWidgetProfiler *profiler,
+ GtkWidget *widget)
+{
+ GtkWidget *window;
+
+ window = ensure_and_get_toplevel (widget);
+ instrument_toplevel (profiler, window);
+
+ return window;
+}
+
+static void
+profile_map_expose (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+ gdouble elapsed;
+
+ priv = profiler->priv;
+
+ g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
+
+ /* Time map.
+ *
+ * FIXME: we are really timing a show_all(); we don't really wait for all the "map_event" signals
+ * to happen. Should we rename GTK_WIDGET_PROFILER_REPORT_MAP report to something else?
+ */
+
+ g_timer_reset (priv->timer);
+
+ gtk_widget_show_all (priv->toplevel);
+ priv->state = STATE_INSTRUMENTED_MAPPED;
+
+ elapsed = g_timer_elapsed (priv->timer, NULL);
+ report (profiler, GTK_WIDGET_PROFILER_REPORT_MAP, elapsed);
+
+ /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
+
+ g_timer_reset (priv->timer);
+ gtk_main ();
+}
+
+static void
+profile_destroy (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+ gdouble elapsed;
+
+ priv = profiler->priv;
+
+ g_assert (priv->state != STATE_NOT_CREATED);
+
+ g_timer_reset (priv->timer);
+ reset_state (profiler);
+ elapsed = g_timer_elapsed (priv->timer, NULL);
+
+ report (profiler, GTK_WIDGET_PROFILER_REPORT_DESTROY, elapsed);
+}
+
+static void
+profile_boot (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+ gdouble elapsed;
+
+ priv = profiler->priv;
+
+ g_assert (priv->state == STATE_NOT_CREATED);
+
+ /* Time creation */
+
+ g_timer_reset (priv->timer);
+
+ priv->profiled_widget = create_widget_via_emission (profiler);
+ priv->toplevel = get_instrumented_toplevel (profiler, priv->profiled_widget);
+
+ priv->state = STATE_INSTRUMENTED_NOT_MAPPED;
+
+ /* Here we include the time to anchor the widget to a toplevel, if
+ * the toplevel was missing --- hopefully not a too-long extra time.
+ */
+
+ elapsed = g_timer_elapsed (priv->timer, NULL);
+ report (profiler, GTK_WIDGET_PROFILER_REPORT_CREATE, elapsed);
+
+ /* Start timing map/expose */
+
+ profile_map_expose (profiler);
+
+ /* Profile destruction */
+
+ profile_destroy (profiler);
+}
+
+void
+gtk_widget_profiler_profile_boot (GtkWidgetProfiler *profiler)
+{
+ GtkWidgetProfilerPrivate *priv;
+ int i, n;
+
+ g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
+
+ priv = profiler->priv;
+ g_return_if_fail (!priv->profiling);
+
+ reset_state (profiler);
+ priv->profiling = TRUE;
+
+ n = priv->n_iterations;
+ for (i = 0; i < n; i++)
+ profile_boot (profiler);
+
+ priv->profiling = FALSE;
+}
diff --git a/perf/gtkwidgetprofiler.h b/perf/gtkwidgetprofiler.h
new file mode 100644
index 0000000000..62d19c8a92
--- /dev/null
+++ b/perf/gtkwidgetprofiler.h
@@ -0,0 +1,58 @@
+#include <gtk/gtk.h>
+
+#ifndef GTK_WIDGET_PROFILER_H
+#define GTK_WIDGET_PROFILER_H
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_WIDGET_PROFILER (gtk_widget_profiler_get_type ())
+#define GTK_WIDGET_PROFILER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WIDGET_PROFILER, GtkWidgetProfiler))
+#define GTK_WIDGET_PROFILER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WIDGET_PROFILER, GtkWidgetProfilerClass))
+#define GTK_IS_WIDGET_PROFILER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WIDGET_PROFILER))
+#define GTK_IS_WIDGET_PROFILER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WIDGET_PROFILER))
+#define GTK_WIDGET_PROFILER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WIDGET_PROFILER, GtkWidgetProfilerClass))
+
+typedef enum
+{
+ GTK_WIDGET_PROFILER_REPORT_CREATE,
+ GTK_WIDGET_PROFILER_REPORT_MAP,
+ GTK_WIDGET_PROFILER_REPORT_EXPOSE,
+ GTK_WIDGET_PROFILER_REPORT_DESTROY
+} GtkWidgetProfilerReport;
+
+typedef struct _GtkWidgetProfiler GtkWidgetProfiler;
+typedef struct _GtkWidgetProfilerClass GtkWidgetProfilerClass;
+typedef struct _GtkWidgetProfilerPrivate GtkWidgetProfilerPrivate;
+
+struct _GtkWidgetProfiler {
+ GObject object;
+
+ GtkWidgetProfilerPrivate *priv;
+};
+
+struct _GtkWidgetProfilerClass {
+ GObjectClass parent_class;
+
+ /* signals */
+
+ GtkWidget *(* create_widget) (GtkWidgetProfiler *profiler);
+
+ void (* report) (GtkWidgetProfiler *profiler,
+ GtkWidgetProfilerReport report,
+ GtkWidget *widget,
+ gdouble elapsed);
+};
+
+GType gtk_widget_profiler_get_type (void) G_GNUC_CONST;
+
+GtkWidgetProfiler *gtk_widget_profiler_new ();
+
+void gtk_widget_profiler_set_num_iterations (GtkWidgetProfiler *profiler,
+ gint n_iterations);
+
+void gtk_widget_profiler_profile_boot (GtkWidgetProfiler *profiler);
+
+
+G_END_DECLS
+
+#endif
diff --git a/perf/main.c b/perf/main.c
index cbd8ded174..303775249d 100644
--- a/perf/main.c
+++ b/perf/main.c
@@ -1,31 +1,35 @@
#include <stdio.h>
#include <gtk/gtk.h>
-#include "appwindow.h"
-#include "timers.h"
+#include "gtkwidgetprofiler.h"
+#include "widgets.h"
-#define ITERS 20
+#define ITERS 10
static GtkWidget *
-create_cb (gpointer data)
+create_widget_cb (GtkWidgetProfiler *profiler, gpointer data)
{
return appwindow_new ();
}
static void
-report_cb (TimerReport report, gdouble elapsed, gpointer data)
+report_cb (GtkWidgetProfiler *profiler, GtkWidgetProfilerReport report, GtkWidget *widget, gdouble elapsed, gpointer data)
{
const char *type;
switch (report) {
- case TIMER_REPORT_WIDGET_CREATION:
+ case GTK_WIDGET_PROFILER_REPORT_CREATE:
type = "widget creation";
break;
- case TIMER_REPORT_WIDGET_SHOW:
- type = "widget show";
+ case GTK_WIDGET_PROFILER_REPORT_MAP:
+ type = "widget map";
break;
- case TIMER_REPORT_WIDGET_DESTRUCTION:
+ case GTK_WIDGET_PROFILER_REPORT_EXPOSE:
+ type = "widget expose";
+ break;
+
+ case GTK_WIDGET_PROFILER_REPORT_DESTROY:
type = "widget destruction";
break;
@@ -36,19 +40,25 @@ report_cb (TimerReport report, gdouble elapsed, gpointer data)
fprintf (stderr, "%s: %g sec\n", type, elapsed);
- if (report == TIMER_REPORT_WIDGET_DESTRUCTION)
+ if (report == GTK_WIDGET_PROFILER_REPORT_DESTROY)
fputs ("\n", stderr);
}
int
main (int argc, char **argv)
{
- int i;
+ GtkWidgetProfiler *profiler;
gtk_init (&argc, &argv);
- for (i = 0; i < ITERS; i++)
- timer_time_widget (create_cb, report_cb, NULL);
+ profiler = gtk_widget_profiler_new ();
+ g_signal_connect (profiler, "create-widget",
+ G_CALLBACK (create_widget_cb), NULL);
+ g_signal_connect (profiler, "report",
+ G_CALLBACK (report_cb), NULL);
+
+ gtk_widget_profiler_set_num_iterations (profiler, ITERS);
+ gtk_widget_profiler_profile_boot (profiler);
return 0;
}
diff --git a/perf/marshalers.list b/perf/marshalers.list
new file mode 100644
index 0000000000..413f2480c6
--- /dev/null
+++ b/perf/marshalers.list
@@ -0,0 +1,2 @@
+OBJECT:VOID
+VOID:ENUM,OBJECT,DOUBLE
diff --git a/perf/textview.c b/perf/textview.c
new file mode 100644
index 0000000000..0590c8af78
--- /dev/null
+++ b/perf/textview.c
@@ -0,0 +1,60 @@
+#include <gtk/gtk.h>
+#include "widgets.h"
+
+GtkWidget *
+text_view_new (void)
+{
+ GtkWidget *sw;
+ GtkWidget *text_view;
+ GtkTextBuffer *buffer;
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+
+ text_view = gtk_text_view_new ();
+ gtk_widget_set_size_request (text_view, 400, 300);
+ gtk_container_add (GTK_CONTAINER (sw), text_view);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ gtk_text_buffer_set_text (buffer,
+ "In felaweshipe, and pilgrimes were they alle,\n"
+ "That toward Caunterbury wolden ryde.\n"
+ "The chambres and the stables weren wyde,\n"
+ "And wel we weren esed atte beste;\n"
+ "And shortly, whan the sonne was to reste,\n"
+ "\n"
+ "So hadde I spoken with hem everychon \n"
+ "That I was of hir felaweshipe anon, \n"
+ "And made forward erly for to ryse \n"
+ "To take our wey, ther as I yow devyse. \n"
+ " But nathelees, whil I have tyme and space, \n"
+ " \n"
+ "Er that I ferther in this tale pace, \n"
+ "Me thynketh it acordaunt to resoun \n"
+ "To telle yow al the condicioun \n"
+ "Of ech of hem, so as it semed me, \n"
+ "And whiche they weren, and of what degree, \n"
+ " \n"
+ "And eek in what array that they were inne; \n"
+ "And at a knyght than wol I first bigynne. \n"
+ " A knyght ther was, and that a worthy man, \n"
+ "That fro the tyme that he first bigan \n"
+ "To riden out, he loved chivalrie, \n"
+ " \n"
+ "Trouthe and honour, fredom and curteisie. \n"
+ "Ful worthy was he in his lordes werre, \n"
+ " \n"
+ "And therto hadde he riden, no man ferre, \n"
+ "As wel in Cristendom as in Hethenesse, \n"
+ "And evere honoured for his worthynesse. \n"
+ " \n"
+ " At Alisaundre he was, whan it was wonne; \n"
+ "Ful ofte tyme he hadde the bord bigonne \n"
+ "Aboven alle nacions in Pruce; \n"
+ "In Lettow hadde he reysed, and in Ruce, \n"
+ "No cristen man so ofte of his degree. \n",
+ -1);
+
+ return sw;
+}
diff --git a/perf/timers.c b/perf/timers.c
deleted file mode 100644
index 042c66b217..0000000000
--- a/perf/timers.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Utility functions for timing widgets
- *
- * Authors:
- * Federico Mena-Quintero <federico@novell.com>
- *
- * To measure how long it takes to fully map and expose a toplevel window, we
- * use a trick which Owen Taylor described on IRC one day:
- *
- * 1. Start a timer.
- * 2. Call gtk_widget_show_all() on the toplevel window.
- * 3. In the expose_event handler of the window, queue an idle handler with
- * G_PRIORITY_HIGH.
- * 4. In the idle handler, change a property on the toplevel window.
- * 5. In the property_notify_event handler, stop the timer.
- */
-
-#include <string.h>
-#include <glib.h>
-#include <gtk/gtkmain.h>
-#include "timers.h"
-
-struct timer_closure
-{
- GTimer *timer;
- GtkWidget *widget;
- TimerReportFunc report_func;
- gpointer user_data;
-};
-
-static gboolean
-widget_property_notify_event_cb (GtkWidget *widget, GdkEventProperty *event, gpointer data)
-{
- struct timer_closure *closure;
- gdouble elapsed;
-
- closure = data;
-
- if (event->atom != gdk_atom_intern ("window_property_change", FALSE))
- return FALSE;
-
- /* Finish timing map/expose */
-
- elapsed = g_timer_elapsed (closure->timer, NULL);
- (* closure->report_func) (TIMER_REPORT_WIDGET_SHOW, elapsed, closure->user_data);
-
- /* Time destruction */
-
- g_timer_reset (closure->timer);
- gtk_widget_destroy (widget);
- elapsed = g_timer_elapsed (closure->timer, NULL);
- (* closure->report_func) (TIMER_REPORT_WIDGET_DESTRUCTION, elapsed, closure->user_data);
-
- gtk_main_quit (); /* This will get us back to the end of timer_time_widget() */
- return TRUE;
-}
-
-static gboolean
-idle_after_expose_cb (gpointer data)
-{
- struct timer_closure *closure;
-
- closure = data;
-
- gdk_property_change (closure->widget->window,
- gdk_atom_intern ("window_property_change", FALSE),
- gdk_atom_intern ("STRING", FALSE),
- 8,
- GDK_PROP_MODE_REPLACE,
- "hello",
- strlen ("hello"));
-
- return FALSE;
-}
-
-static gboolean
-widget_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
-{
- struct timer_closure *closure;
-
- closure = data;
-
- g_idle_add_full (G_PRIORITY_HIGH, idle_after_expose_cb, closure, NULL);
- return FALSE;
-}
-
-static void
-instrument_widget (GtkWidget *widget,
- struct timer_closure *closure)
-{
- g_signal_connect (widget, "expose-event",
- G_CALLBACK (widget_expose_event_cb), closure);
-
- gtk_widget_add_events (widget, GDK_PROPERTY_CHANGE_MASK);
- g_signal_connect (widget, "property-notify-event",
- G_CALLBACK (widget_property_notify_event_cb), closure);
-}
-
-void
-timer_time_widget (TimerWidgetCreateFunc create_func,
- TimerReportFunc report_func,
- gpointer user_data)
-{
- GTimer *timer;
- GtkWidget *widget;
- gdouble elapsed;
- struct timer_closure closure;
-
- g_assert (create_func != NULL);
- g_assert (report_func != NULL);
-
- /* Time creation */
-
- timer = g_timer_new ();
- widget = (* create_func) (user_data);
- g_assert (widget != NULL);
- g_assert (!GTK_WIDGET_VISIBLE (widget) && !GTK_WIDGET_MAPPED (widget));
- elapsed = g_timer_elapsed (timer, NULL);
-
- (* report_func) (TIMER_REPORT_WIDGET_CREATION, elapsed, user_data);
-
- /* Start timing map/expose */
-
- closure.timer = timer;
- closure.widget = widget;
- closure.report_func = report_func;
- closure.user_data = user_data;
- instrument_widget (widget, &closure);
-
- g_timer_reset (timer);
- gtk_widget_show_all (widget);
- gtk_main ();
-
- /* Expose time and destruction time get recorded in widget_property_notify_event_cb() */
-}
diff --git a/perf/timers.h b/perf/timers.h
deleted file mode 100644
index d00d6cc611..0000000000
--- a/perf/timers.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <gtk/gtkwidget.h>
-
-typedef enum
-{
- TIMER_REPORT_WIDGET_CREATION,
- TIMER_REPORT_WIDGET_SHOW,
- TIMER_REPORT_WIDGET_DESTRUCTION
-} TimerReport;
-
-typedef GtkWidget *(* TimerWidgetCreateFunc) (gpointer user_data);
-
-typedef void (* TimerReportFunc) (TimerReport report, gdouble elapsed, gpointer user_data);
-
-void timer_time_widget (TimerWidgetCreateFunc create_func,
- TimerReportFunc report_func,
- gpointer user_data);
diff --git a/perf/treeview.c b/perf/treeview.c
new file mode 100644
index 0000000000..461a67a377
--- /dev/null
+++ b/perf/treeview.c
@@ -0,0 +1,101 @@
+#include <gtk/gtk.h>
+#include "widgets.h"
+
+struct row_data {
+ char *stock_id;
+ char *text1;
+ char *text2;
+};
+
+static struct row_data row_data[] = {
+ { GTK_STOCK_NEW, "First", "Here bygynneth the Book of the tales of Caunterbury." },
+ { GTK_STOCK_OPEN, "Second", "Whan that Aprille, with hise shoures soote," },
+ { GTK_STOCK_ABOUT, "Third", "The droghte of March hath perced to the roote" },
+ { GTK_STOCK_ADD, "Fourth", "And bathed every veyne in swich licour," },
+ { GTK_STOCK_APPLY, "Fifth", "Of which vertu engendred is the flour;" },
+ { GTK_STOCK_BOLD, "Sixth", "Whan Zephirus eek with his swete breeth" },
+ { GTK_STOCK_CANCEL, "Seventh", "Inspired hath in every holt and heeth" },
+ { GTK_STOCK_CDROM, "Eighth", "The tendre croppes, and the yonge sonne" },
+ { GTK_STOCK_CLEAR, "Ninth", "Hath in the Ram his halfe cours yronne," },
+ { GTK_STOCK_CLOSE, "Tenth", "And smale foweles maken melodye," },
+ { GTK_STOCK_COLOR_PICKER, "Eleventh", "That slepen al the nyght with open eye-" },
+ { GTK_STOCK_CONVERT, "Twelfth", "So priketh hem Nature in hir corages-" },
+ { GTK_STOCK_CONNECT, "Thirteenth", "Thanne longen folk to goon on pilgrimages" },
+ { GTK_STOCK_COPY, "Fourteenth", "And palmeres for to seken straunge strondes" },
+ { GTK_STOCK_CUT, "Fifteenth", "To ferne halwes, kowthe in sondry londes;" },
+ { GTK_STOCK_DELETE, "Sixteenth", "And specially, from every shires ende" },
+ { GTK_STOCK_DIRECTORY, "Seventeenth", "Of Engelond, to Caunturbury they wende," },
+ { GTK_STOCK_DISCONNECT, "Eighteenth", "The hooly blisful martir for the seke" },
+ { GTK_STOCK_EDIT, "Nineteenth", "That hem hath holpen, whan that they were seeke." },
+ { GTK_STOCK_EXECUTE, "Twentieth", "Bifil that in that seson, on a day," },
+ { GTK_STOCK_FILE, "Twenty-first", "In Southwerk at the Tabard as I lay," },
+ { GTK_STOCK_FIND, "Twenty-second", "Redy to wenden on my pilgrymage" },
+ { GTK_STOCK_FIND_AND_REPLACE, "Twenty-third", "To Caunterbury, with ful devout corage," },
+ { GTK_STOCK_FLOPPY, "Twenty-fourth", "At nyght were come into that hostelrye" },
+ { GTK_STOCK_FULLSCREEN, "Twenty-fifth", "Wel nyne and twenty in a compaignye" },
+ { GTK_STOCK_GOTO_BOTTOM, "Twenty-sixth", "Of sondry folk, by aventure yfalle" },
+};
+
+static GtkTreeModel *
+tree_model_new (void)
+{
+ GtkListStore *list;
+ int i;
+
+ list = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+ for (i = 0; i < G_N_ELEMENTS (row_data); i++)
+ {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (list, &iter);
+ gtk_list_store_set (list,
+ &iter,
+ 0, row_data[i].stock_id,
+ 1, row_data[i].text1,
+ 2, row_data[i].text2,
+ -1);
+ }
+
+ return GTK_TREE_MODEL (list);
+}
+
+GtkWidget *
+tree_view_new (void)
+{
+ GtkWidget *sw;
+ GtkWidget *tree;
+ GtkTreeModel *model;
+ GtkTreeViewColumn *column;
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+
+ model = tree_model_new ();
+ tree = gtk_tree_view_new_with_model (model);
+ g_object_unref (model);
+
+ gtk_widget_set_size_request (tree, 300, 100);
+
+ column = gtk_tree_view_column_new_with_attributes ("Icon",
+ gtk_cell_renderer_pixbuf_new (),
+ "stock-id", 0,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Index",
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+
+ column = gtk_tree_view_column_new_with_attributes ("Canterbury Tales",
+ gtk_cell_renderer_text_new (),
+ "text", 2,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+
+ gtk_container_add (GTK_CONTAINER (sw), tree);
+
+ return sw;
+}
diff --git a/perf/widgets.h b/perf/widgets.h
new file mode 100644
index 0000000000..349b131066
--- /dev/null
+++ b/perf/widgets.h
@@ -0,0 +1,7 @@
+#include <gtk/gtkwidget.h>
+
+GtkWidget *appwindow_new (void);
+
+GtkWidget *text_view_new (void);
+
+GtkWidget *tree_view_new (void);