3 #include "gtkwidgetprofiler.h"
4 #include "typebuiltins.h"
8 STATE_INSTRUMENTED_NOT_MAPPED,
9 STATE_INSTRUMENTED_MAPPED
12 struct _GtkWidgetProfilerPrivate {
15 GtkWidget *profiled_widget;
22 gulong toplevel_draw_id;
23 gulong toplevel_property_notify_event_id;
25 GdkAtom profiler_atom;
30 G_DEFINE_TYPE (GtkWidgetProfiler, gtk_widget_profiler, G_TYPE_OBJECT);
32 static void gtk_widget_profiler_finalize (GObject *object);
40 static guint signals[LAST_SIGNAL];
43 gtk_widget_profiler_class_init (GtkWidgetProfilerClass *class)
45 GObjectClass *object_class;
47 object_class = (GObjectClass *) class;
49 signals[CREATE_WIDGET] =
50 g_signal_new ("create-widget",
51 G_OBJECT_CLASS_TYPE (object_class),
53 G_STRUCT_OFFSET (GtkWidgetProfilerClass, create_widget),
59 g_signal_new ("report",
60 G_OBJECT_CLASS_TYPE (object_class),
62 G_STRUCT_OFFSET (GtkWidgetProfilerClass, report),
66 GTK_TYPE_WIDGET_PROFILER_REPORT,
70 object_class->finalize = gtk_widget_profiler_finalize;
74 gtk_widget_profiler_init (GtkWidgetProfiler *profiler)
76 GtkWidgetProfilerPrivate *priv;
78 priv = g_new0 (GtkWidgetProfilerPrivate, 1);
79 profiler->priv = priv;
81 priv->state = STATE_NOT_CREATED;
82 priv->n_iterations = 1;
84 priv->timer = g_timer_new ();
86 priv->profiler_atom = gdk_atom_intern ("GtkWidgetProfiler", FALSE);
90 reset_state (GtkWidgetProfiler *profiler)
92 GtkWidgetProfilerPrivate *priv;
94 priv = profiler->priv;
98 g_signal_handler_disconnect (priv->toplevel, priv->toplevel_draw_id);
99 priv->toplevel_draw_id = 0;
101 g_signal_handler_disconnect (priv->toplevel, priv->toplevel_property_notify_event_id);
102 priv->toplevel_property_notify_event_id = 0;
104 gtk_widget_destroy (priv->toplevel);
105 priv->toplevel = NULL;
106 priv->profiled_widget = NULL;
109 priv->state = STATE_NOT_CREATED;
113 gtk_widget_profiler_finalize (GObject *object)
115 GtkWidgetProfiler *profiler;
116 GtkWidgetProfilerPrivate *priv;
118 profiler = GTK_WIDGET_PROFILER (object);
119 priv = profiler->priv;
121 reset_state (profiler);
122 g_timer_destroy (priv->timer);
126 G_OBJECT_CLASS (gtk_widget_profiler_parent_class)->finalize (object);
130 gtk_widget_profiler_new (void)
132 return g_object_new (GTK_TYPE_WIDGET_PROFILER, NULL);
136 gtk_widget_profiler_set_num_iterations (GtkWidgetProfiler *profiler,
139 GtkWidgetProfilerPrivate *priv;
141 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
142 g_return_if_fail (n_iterations > 0);
144 priv = profiler->priv;
145 priv->n_iterations = n_iterations;
149 report (GtkWidgetProfiler *profiler,
150 GtkWidgetProfilerReport report,
153 GtkWidgetProfilerPrivate *priv;
155 priv = profiler->priv;
157 g_signal_emit (profiler, signals[REPORT], 0, report, priv->profiled_widget, elapsed);
161 create_widget_via_emission (GtkWidgetProfiler *profiler)
166 g_signal_emit (profiler, signals[CREATE_WIDGET], 0, &widget);
168 g_error ("The profiler emitted the \"create-widget\" signal but the signal handler returned no widget!");
170 if (gtk_widget_get_visible (widget) || gtk_widget_get_mapped (widget))
171 g_error ("The handler for \"create-widget\" must return an unmapped and unshown widget");
177 toplevel_property_notify_event_cb (GtkWidget *widget, GdkEventProperty *event, gpointer data)
179 GtkWidgetProfiler *profiler;
180 GtkWidgetProfilerPrivate *priv;
183 profiler = GTK_WIDGET_PROFILER (data);
184 priv = profiler->priv;
186 if (event->atom != priv->profiler_atom)
189 /* Finish timing map/expose */
191 elapsed = g_timer_elapsed (priv->timer, NULL);
192 report (profiler, GTK_WIDGET_PROFILER_REPORT_EXPOSE, elapsed);
194 gtk_main_quit (); /* This will get us back to the end of profile_map_expose() */
199 toplevel_idle_after_draw_cb (gpointer data)
201 GtkWidgetProfiler *profiler;
202 GtkWidgetProfilerPrivate *priv;
204 profiler = GTK_WIDGET_PROFILER (data);
205 priv = profiler->priv;
207 gdk_property_change (gtk_widget_get_window (priv->toplevel),
209 gdk_atom_intern ("STRING", FALSE),
211 GDK_PROP_MODE_REPLACE,
219 toplevel_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
221 GtkWidgetProfiler *profiler;
223 profiler = GTK_WIDGET_PROFILER (data);
225 gdk_threads_add_idle_full (G_PRIORITY_HIGH, toplevel_idle_after_draw_cb, profiler, NULL);
230 instrument_toplevel (GtkWidgetProfiler *profiler,
233 GtkWidgetProfilerPrivate *priv;
235 priv = profiler->priv;
237 priv->toplevel_draw_id = g_signal_connect (toplevel, "draw",
238 G_CALLBACK (toplevel_draw_cb), profiler);
240 gtk_widget_add_events (toplevel, GDK_PROPERTY_CHANGE_MASK);
241 priv->toplevel_property_notify_event_id = g_signal_connect (toplevel, "property-notify-event",
242 G_CALLBACK (toplevel_property_notify_event_cb), profiler);
246 ensure_and_get_toplevel (GtkWidget *widget)
251 toplevel = gtk_widget_get_toplevel (widget);
252 if (gtk_widget_is_toplevel (toplevel))
255 g_assert (toplevel == widget); /* we don't want extraneous ancestors */
257 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
258 gtk_container_add (GTK_CONTAINER (window), widget);
264 get_instrumented_toplevel (GtkWidgetProfiler *profiler,
269 window = ensure_and_get_toplevel (widget);
270 instrument_toplevel (profiler, window);
276 map_widget (GtkWidgetProfiler *profiler)
278 GtkWidgetProfilerPrivate *priv;
280 priv = profiler->priv;
281 g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
285 * FIXME: we are really timing a show_all(); we don't really wait for all the "map_event" signals
286 * to happen. Should we rename GTK_WIDGET_PROFILER_REPORT_MAP report to something else?
289 gtk_widget_show_all (priv->toplevel);
290 priv->state = STATE_INSTRUMENTED_MAPPED;
294 profile_map_expose (GtkWidgetProfiler *profiler)
296 GtkWidgetProfilerPrivate *priv;
299 priv = profiler->priv;
301 g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
303 g_timer_reset (priv->timer);
304 map_widget (profiler);
305 elapsed = g_timer_elapsed (priv->timer, NULL);
307 report (profiler, GTK_WIDGET_PROFILER_REPORT_MAP, elapsed);
309 /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
311 g_timer_reset (priv->timer);
316 profile_destroy (GtkWidgetProfiler *profiler)
318 GtkWidgetProfilerPrivate *priv;
321 priv = profiler->priv;
323 g_assert (priv->state != STATE_NOT_CREATED);
325 g_timer_reset (priv->timer);
326 reset_state (profiler);
327 elapsed = g_timer_elapsed (priv->timer, NULL);
329 report (profiler, GTK_WIDGET_PROFILER_REPORT_DESTROY, elapsed);
333 create_widget (GtkWidgetProfiler *profiler)
335 GtkWidgetProfilerPrivate *priv;
337 priv = profiler->priv;
339 g_assert (priv->state == STATE_NOT_CREATED);
341 priv->profiled_widget = create_widget_via_emission (profiler);
342 priv->toplevel = get_instrumented_toplevel (profiler, priv->profiled_widget);
344 priv->state = STATE_INSTRUMENTED_NOT_MAPPED;
347 /* The "boot time" of a widget is the time needed to
349 * 1. Create the widget
354 * This runs a lot of interesting code: instantiation, size requisition and
355 * allocation, realization, mapping, exposing, destruction.
358 profile_boot (GtkWidgetProfiler *profiler)
360 GtkWidgetProfilerPrivate *priv;
363 priv = profiler->priv;
365 g_assert (priv->state == STATE_NOT_CREATED);
369 g_timer_reset (priv->timer);
370 create_widget (profiler);
371 elapsed = g_timer_elapsed (priv->timer, NULL);
373 report (profiler, GTK_WIDGET_PROFILER_REPORT_CREATE, elapsed);
375 /* Start timing map/expose */
377 profile_map_expose (profiler);
379 /* Profile destruction */
381 profile_destroy (profiler);
384 /* To measure expose time, we trigger a full expose on the toplevel window. We
385 * do the same as xrefresh(1), i.e. we map and unmap a window to make the other
389 profile_expose (GtkWidgetProfiler *profiler)
391 GtkWidgetProfilerPrivate *priv;
392 GtkAllocation allocation;
397 priv = profiler->priv;
399 g_assert (priv->state == STATE_INSTRUMENTED_MAPPED);
403 gtk_widget_get_allocation (priv->toplevel, &allocation);
407 attr.width = allocation.width;
408 attr.height = allocation.width;
409 attr.wclass = GDK_INPUT_OUTPUT;
410 attr.window_type = GDK_WINDOW_TEMP;
412 attr_mask = GDK_WA_X | GDK_WA_Y;
414 window = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (priv->toplevel)),
417 gdk_window_show (window);
418 gdk_window_hide (window);
419 gdk_window_destroy (window);
421 /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
423 g_timer_reset (priv->timer);
428 gtk_widget_profiler_profile_boot (GtkWidgetProfiler *profiler)
430 GtkWidgetProfilerPrivate *priv;
433 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
435 priv = profiler->priv;
436 g_return_if_fail (!priv->profiling);
438 reset_state (profiler);
439 priv->profiling = TRUE;
441 n = priv->n_iterations;
442 for (i = 0; i < n; i++)
443 profile_boot (profiler);
445 priv->profiling = FALSE;
449 gtk_widget_profiler_profile_expose (GtkWidgetProfiler *profiler)
451 GtkWidgetProfilerPrivate *priv;
454 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
456 priv = profiler->priv;
457 g_return_if_fail (!priv->profiling);
459 reset_state (profiler);
460 priv->profiling = TRUE;
462 create_widget (profiler);
463 map_widget (profiler);
465 n = priv->n_iterations;
466 for (i = 0; i < n; i++)
467 profile_expose (profiler);
469 priv->profiling = FALSE;
471 reset_state (profiler);