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.
This commit is contained in:
Federico Mena Quintero 2005-07-29 00:38:51 +00:00 committed by Federico Mena Quintero
parent 375cb32bc1
commit b4f52020f0
16 changed files with 895 additions and 423 deletions

View File

@ -1,3 +1,39 @@
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.
2005-07-29 Tor Lillqvist <tml@novell.com>
* gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating

View File

@ -1,3 +1,39 @@
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.
2005-07-29 Tor Lillqvist <tml@novell.com>
* gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating

View File

@ -1,3 +1,39 @@
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.
2005-07-29 Tor Lillqvist <tml@novell.com>
* gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating

View File

@ -28,12 +28,73 @@ testperf_DEPENDENCIES = $(TEST_DEPS)
testperf_LDADD = $(LDADDS)
testperf_SOURCES = \
appwindow.c \
appwindow.h \
main.c \
timers.c \
timers.h
testperf_SOURCES = \
appwindow.c \
gtkwidgetprofiler.c \
gtkwidgetprofiler.h \
main.c \
marshalers.c \
marshalers.h \
textview.c \
treeview.c \
typebuiltins.c \
typebuiltins.h \
widgets.h
EXTRA_DIST = \
README
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)

View File

@ -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.

View File

@ -13,6 +13,8 @@
#include <string.h>
#include <gtk/gtk.h>
#include "widgets.h"
static void
quit_cb (GtkWidget *widget, gpointer data)
{
@ -150,230 +152,38 @@ 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)
static GtkWidget *
drawing_area_new (void)
{
GtkListStore *list;
int i;
GtkWidget *darea;
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);
darea = gtk_drawing_area_new ();
gtk_widget_set_size_request (darea, 640, 480);
return darea;
}
static GtkWidget *
tree_view_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 ();
}
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;
}
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)
{
@ -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;
}

View File

@ -1,3 +0,0 @@
#include <gtk/gtkwidget.h>
GtkWidget *appwindow_new (void);

377
perf/gtkwidgetprofiler.c Normal file
View File

@ -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;
}

58
perf/gtkwidgetprofiler.h Normal file
View File

@ -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

View File

@ -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;
}

2
perf/marshalers.list Normal file
View File

@ -0,0 +1,2 @@
OBJECT:VOID
VOID:ENUM,OBJECT,DOUBLE

60
perf/textview.c Normal file
View File

@ -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;
}

View File

@ -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() */
}

View File

@ -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);

101
perf/treeview.c Normal file
View File

@ -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;
}

7
perf/widgets.h Normal file
View File

@ -0,0 +1,7 @@
#include <gtk/gtkwidget.h>
GtkWidget *appwindow_new (void);
GtkWidget *text_view_new (void);
GtkWidget *tree_view_new (void);