diff --git a/ChangeLog b/ChangeLog index 58945460d8..ba8b33c0dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2005-07-28 Federico Mena Quintero + + * 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 * gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 58945460d8..ba8b33c0dd 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,39 @@ +2005-07-28 Federico Mena Quintero + + * 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 * gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 58945460d8..ba8b33c0dd 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,39 @@ +2005-07-28 Federico Mena Quintero + + * 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 * gdk/win32/gdkevents-win32.c: Remove ifdeffed out code relating 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 +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 \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 #include -#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 #include +#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; } 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 - -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 +#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 + +#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 #include -#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 +#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 - * - * 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 -#include -#include -#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 - -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 +#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 + +GtkWidget *appwindow_new (void); + +GtkWidget *text_view_new (void); + +GtkWidget *tree_view_new (void);