3 #include "gtkwidgetprofiler.h"
4 #include "marshalers.h"
5 #include "typebuiltins.h"
9 STATE_INSTRUMENTED_NOT_MAPPED,
10 STATE_INSTRUMENTED_MAPPED
13 struct _GtkWidgetProfilerPrivate {
16 GtkWidget *profiled_widget;
23 gulong toplevel_expose_event_id;
24 gulong toplevel_property_notify_event_id;
26 GdkAtom profiler_atom;
31 G_DEFINE_TYPE (GtkWidgetProfiler, gtk_widget_profiler, G_TYPE_OBJECT);
33 static void gtk_widget_profiler_finalize (GObject *object);
41 static guint signals[LAST_SIGNAL];
44 gtk_widget_profiler_class_init (GtkWidgetProfilerClass *class)
46 GObjectClass *object_class;
48 object_class = (GObjectClass *) class;
50 signals[CREATE_WIDGET] =
51 g_signal_new ("create-widget",
52 G_OBJECT_CLASS_TYPE (object_class),
54 G_STRUCT_OFFSET (GtkWidgetProfilerClass, create_widget),
56 _gtk_marshal_OBJECT__VOID,
60 g_signal_new ("report",
61 G_OBJECT_CLASS_TYPE (object_class),
63 G_STRUCT_OFFSET (GtkWidgetProfilerClass, report),
65 _gtk_marshal_VOID__ENUM_OBJECT_DOUBLE,
67 GTK_TYPE_WIDGET_PROFILER_REPORT,
71 object_class->finalize = gtk_widget_profiler_finalize;
75 gtk_widget_profiler_init (GtkWidgetProfiler *profiler)
77 GtkWidgetProfilerPrivate *priv;
79 priv = g_new0 (GtkWidgetProfilerPrivate, 1);
80 profiler->priv = priv;
82 priv->state = STATE_NOT_CREATED;
83 priv->n_iterations = 1;
85 priv->timer = g_timer_new ();
87 priv->profiler_atom = gdk_atom_intern ("GtkWidgetProfiler", FALSE);
91 reset_state (GtkWidgetProfiler *profiler)
93 GtkWidgetProfilerPrivate *priv;
95 priv = profiler->priv;
99 g_signal_handler_disconnect (priv->toplevel, priv->toplevel_expose_event_id);
100 priv->toplevel_expose_event_id = 0;
102 g_signal_handler_disconnect (priv->toplevel, priv->toplevel_property_notify_event_id);
103 priv->toplevel_property_notify_event_id = 0;
105 gtk_widget_destroy (priv->toplevel);
106 priv->toplevel = NULL;
107 priv->profiled_widget = NULL;
110 priv->state = STATE_NOT_CREATED;
114 gtk_widget_profiler_finalize (GObject *object)
116 GtkWidgetProfiler *profiler;
117 GtkWidgetProfilerPrivate *priv;
119 profiler = GTK_WIDGET_PROFILER (object);
120 priv = profiler->priv;
122 reset_state (profiler);
123 g_timer_destroy (priv->timer);
127 G_OBJECT_CLASS (gtk_widget_profiler_parent_class)->finalize (object);
131 gtk_widget_profiler_new (void)
133 return g_object_new (GTK_TYPE_WIDGET_PROFILER, NULL);
137 gtk_widget_profiler_set_num_iterations (GtkWidgetProfiler *profiler,
140 GtkWidgetProfilerPrivate *priv;
142 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
143 g_return_if_fail (n_iterations > 0);
145 priv = profiler->priv;
146 priv->n_iterations = n_iterations;
150 report (GtkWidgetProfiler *profiler,
151 GtkWidgetProfilerReport report,
154 GtkWidgetProfilerPrivate *priv;
156 priv = profiler->priv;
158 g_signal_emit (profiler, signals[REPORT], 0, report, priv->profiled_widget, elapsed);
162 create_widget_via_emission (GtkWidgetProfiler *profiler)
167 g_signal_emit (profiler, signals[CREATE_WIDGET], 0, &widget);
169 g_error ("The profiler emitted the \"create-widget\" signal but the signal handler returned no widget!");
171 if (GTK_WIDGET_VISIBLE (widget) || GTK_WIDGET_MAPPED (widget))
172 g_error ("The handler for \"create-widget\" must return an unmapped and unshown widget");
178 toplevel_property_notify_event_cb (GtkWidget *widget, GdkEventProperty *event, gpointer data)
180 GtkWidgetProfiler *profiler;
181 GtkWidgetProfilerPrivate *priv;
184 profiler = GTK_WIDGET_PROFILER (data);
185 priv = profiler->priv;
187 if (event->atom != priv->profiler_atom)
190 /* Finish timing map/expose */
192 elapsed = g_timer_elapsed (priv->timer, NULL);
193 report (profiler, GTK_WIDGET_PROFILER_REPORT_EXPOSE, elapsed);
195 gtk_main_quit (); /* This will get us back to the end of profile_map_expose() */
200 toplevel_idle_after_expose_cb (gpointer data)
202 GtkWidgetProfiler *profiler;
203 GtkWidgetProfilerPrivate *priv;
205 profiler = GTK_WIDGET_PROFILER (data);
206 priv = profiler->priv;
208 gdk_property_change (priv->toplevel->window,
210 gdk_atom_intern ("STRING", FALSE),
212 GDK_PROP_MODE_REPLACE,
220 toplevel_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data)
222 GtkWidgetProfiler *profiler;
224 profiler = GTK_WIDGET_PROFILER (data);
226 gdk_threads_add_idle_full (G_PRIORITY_HIGH, toplevel_idle_after_expose_cb, profiler, NULL);
231 instrument_toplevel (GtkWidgetProfiler *profiler,
234 GtkWidgetProfilerPrivate *priv;
236 priv = profiler->priv;
238 priv->toplevel_expose_event_id = g_signal_connect (toplevel, "expose-event",
239 G_CALLBACK (toplevel_expose_event_cb), profiler);
241 gtk_widget_add_events (toplevel, GDK_PROPERTY_CHANGE_MASK);
242 priv->toplevel_property_notify_event_id = g_signal_connect (toplevel, "property-notify-event",
243 G_CALLBACK (toplevel_property_notify_event_cb), profiler);
247 ensure_and_get_toplevel (GtkWidget *widget)
252 toplevel = gtk_widget_get_toplevel (widget);
253 if (GTK_WIDGET_TOPLEVEL (toplevel))
256 g_assert (toplevel == widget); /* we don't want extraneous ancestors */
258 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
259 gtk_container_add (GTK_CONTAINER (window), widget);
265 get_instrumented_toplevel (GtkWidgetProfiler *profiler,
270 window = ensure_and_get_toplevel (widget);
271 instrument_toplevel (profiler, window);
277 map_widget (GtkWidgetProfiler *profiler)
279 GtkWidgetProfilerPrivate *priv;
281 priv = profiler->priv;
282 g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
286 * FIXME: we are really timing a show_all(); we don't really wait for all the "map_event" signals
287 * to happen. Should we rename GTK_WIDGET_PROFILER_REPORT_MAP report to something else?
290 gtk_widget_show_all (priv->toplevel);
291 priv->state = STATE_INSTRUMENTED_MAPPED;
295 profile_map_expose (GtkWidgetProfiler *profiler)
297 GtkWidgetProfilerPrivate *priv;
300 priv = profiler->priv;
302 g_assert (priv->state == STATE_INSTRUMENTED_NOT_MAPPED);
304 g_timer_reset (priv->timer);
305 map_widget (profiler);
306 elapsed = g_timer_elapsed (priv->timer, NULL);
308 report (profiler, GTK_WIDGET_PROFILER_REPORT_MAP, elapsed);
310 /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
312 g_timer_reset (priv->timer);
317 profile_destroy (GtkWidgetProfiler *profiler)
319 GtkWidgetProfilerPrivate *priv;
322 priv = profiler->priv;
324 g_assert (priv->state != STATE_NOT_CREATED);
326 g_timer_reset (priv->timer);
327 reset_state (profiler);
328 elapsed = g_timer_elapsed (priv->timer, NULL);
330 report (profiler, GTK_WIDGET_PROFILER_REPORT_DESTROY, elapsed);
334 create_widget (GtkWidgetProfiler *profiler)
336 GtkWidgetProfilerPrivate *priv;
338 priv = profiler->priv;
340 g_assert (priv->state == STATE_NOT_CREATED);
342 priv->profiled_widget = create_widget_via_emission (profiler);
343 priv->toplevel = get_instrumented_toplevel (profiler, priv->profiled_widget);
345 priv->state = STATE_INSTRUMENTED_NOT_MAPPED;
348 /* The "boot time" of a widget is the time needed to
350 * 1. Create the widget
355 * This runs a lot of interesting code: instantiation, size requisition and
356 * allocation, realization, mapping, exposing, destruction.
359 profile_boot (GtkWidgetProfiler *profiler)
361 GtkWidgetProfilerPrivate *priv;
364 priv = profiler->priv;
366 g_assert (priv->state == STATE_NOT_CREATED);
370 g_timer_reset (priv->timer);
371 create_widget (profiler);
372 elapsed = g_timer_elapsed (priv->timer, NULL);
374 report (profiler, GTK_WIDGET_PROFILER_REPORT_CREATE, elapsed);
376 /* Start timing map/expose */
378 profile_map_expose (profiler);
380 /* Profile destruction */
382 profile_destroy (profiler);
385 /* To measure expose time, we trigger a full expose on the toplevel window. We
386 * do the same as xrefresh(1), i.e. we map and unmap a window to make the other
390 profile_expose (GtkWidgetProfiler *profiler)
392 GtkWidgetProfilerPrivate *priv;
397 priv = profiler->priv;
399 g_assert (priv->state == STATE_INSTRUMENTED_MAPPED);
405 attr.width = priv->toplevel->allocation.width;
406 attr.height = priv->toplevel->allocation.width;
407 attr.wclass = GDK_INPUT_OUTPUT;
408 attr.window_type = GDK_WINDOW_CHILD;
410 attr_mask = GDK_WA_X | GDK_WA_Y;
412 window = gdk_window_new (priv->toplevel->window, &attr, attr_mask);
413 gdk_window_set_back_pixmap (window, NULL, TRUE); /* avoid flicker */
415 gdk_window_show (window);
416 gdk_window_hide (window);
417 gdk_window_destroy (window);
419 /* Time expose; this gets recorded in toplevel_property_notify_event_cb() */
421 g_timer_reset (priv->timer);
426 gtk_widget_profiler_profile_boot (GtkWidgetProfiler *profiler)
428 GtkWidgetProfilerPrivate *priv;
431 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
433 priv = profiler->priv;
434 g_return_if_fail (!priv->profiling);
436 reset_state (profiler);
437 priv->profiling = TRUE;
439 n = priv->n_iterations;
440 for (i = 0; i < n; i++)
441 profile_boot (profiler);
443 priv->profiling = FALSE;
447 gtk_widget_profiler_profile_expose (GtkWidgetProfiler *profiler)
449 GtkWidgetProfilerPrivate *priv;
452 g_return_if_fail (GTK_IS_WIDGET_PROFILER (profiler));
454 priv = profiler->priv;
455 g_return_if_fail (!priv->profiling);
457 reset_state (profiler);
458 priv->profiling = TRUE;
460 create_widget (profiler);
461 map_widget (profiler);
463 n = priv->n_iterations;
464 for (i = 0; i < n; i++)
465 profile_expose (profiler);
467 priv->profiling = FALSE;
469 reset_state (profiler);