diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 8c15e16633..acabe1f990 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -27,6 +27,7 @@ demos_base = \ event_axes.c \ expander.c \ filtermodel.c \ + fishbowl.c \ foreigndrawing.c \ gestures.c \ glarea.c \ diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index f0c7201f3c..7a44d6d058 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -94,6 +94,9 @@ zoom_in_cursor.png zoom_out_cursor.png + + fishbowl.ui + gnome-fs-directory.png gnome-fs-regular.png @@ -150,6 +153,7 @@ event_axes.c expander.c filtermodel.c + fishbowl.c flowbox.c foreigndrawing.c font_features.c diff --git a/demos/gtk-demo/fishbowl.c b/demos/gtk-demo/fishbowl.c new file mode 100644 index 0000000000..9350e4e5c2 --- /dev/null +++ b/demos/gtk-demo/fishbowl.c @@ -0,0 +1,334 @@ +/* Benchmark/Fishbowl + * + * This demo models the fishbowl demos seen on the web in a GTK way. + * It's also a neat little tool to see how fast your computer (or + * your GTK version) is. + */ + +#include + +char **icon_names = NULL; +gsize n_icon_names = 0; + +static void +init_icon_names (GtkIconTheme *theme) +{ + GPtrArray *icons; + GList *l, *icon_list; + + if (icon_names) + return; + + icon_list = gtk_icon_theme_list_icons (theme, NULL); + icons = g_ptr_array_new (); + + for (l = icon_list; l; l = l->next) + { + if (g_str_has_suffix (l->data, "-symbolic")) + continue; + + g_ptr_array_add (icons, g_strdup (l->data)); + } + + n_icon_names = icons->len; + g_ptr_array_add (icons, NULL); /* NULL-terminate the array */ + icon_names = (char **) g_ptr_array_free (icons, FALSE); + + /* don't free strings, we assigned them to the array */ + g_list_free_full (icon_list, g_free); +} + +static const char * +get_random_icon_name (GtkIconTheme *theme) +{ + init_icon_names (theme); + + return icon_names[g_random_int_range(0, n_icon_names)]; +} + +#define N_STATS 5 + +#define STATS_UPDATE_TIME G_USEC_PER_SEC + +typedef struct _Stats Stats; +struct _Stats { + gint64 last_stats; + gint64 last_frame; + gint last_suggestion; + guint frame_counter_max; + + guint stats_index; + guint frame_counter[N_STATS]; + guint item_counter[N_STATS]; +}; + +static Stats * +get_stats (GtkWidget *widget) +{ + static GQuark stats_quark = 0; + Stats *stats; + + if (G_UNLIKELY (stats_quark == 0)) + stats_quark = g_quark_from_static_string ("stats"); + + stats = g_object_get_qdata (G_OBJECT (widget), stats_quark); + if (stats == NULL) + { + stats = g_new0 (Stats, 1); + g_object_set_qdata_full (G_OBJECT (widget), stats_quark, stats, g_free); + stats->last_frame = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)); + stats->last_stats = stats->last_frame; + } + + return stats; +} + +static gint64 +do_stats (GtkWidget *widget, + GtkWidget *info_label, + gint *suggested_change) +{ + Stats *stats; + gint64 frame_time, elapsed; + + stats = get_stats (widget); + frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)); + elapsed = frame_time - stats->last_frame; + + if (stats->last_stats + STATS_UPDATE_TIME < frame_time) + { + char *new_label; + guint i, n_frames; + + n_frames = 0; + for (i = 0; i < N_STATS; i++) + { + n_frames += stats->frame_counter[i]; + } + + new_label = g_strdup_printf ("%u icons - %.1f fps", + stats->item_counter[stats->stats_index], + (double) G_USEC_PER_SEC * n_frames + / (N_STATS * STATS_UPDATE_TIME)); + gtk_label_set_label (GTK_LABEL (info_label), new_label); + g_free (new_label); + + if (stats->frame_counter[stats->stats_index] >= 19 * stats->frame_counter_max / 20) + { + if (stats->last_suggestion > 0) + stats->last_suggestion *= 2; + else + stats->last_suggestion = 1; + *suggested_change = stats->last_suggestion; + } + else + { + if (stats->last_suggestion < 0) + stats->last_suggestion--; + else + stats->last_suggestion = -1; + stats->last_suggestion = MAX (stats->last_suggestion, 1 - (int) stats->item_counter[stats->stats_index]); + *suggested_change = stats->last_suggestion; + } + + stats->stats_index = (stats->stats_index + 1) % N_STATS; + stats->frame_counter[stats->stats_index] = 0; + stats->item_counter[stats->stats_index] = stats->item_counter[(stats->stats_index + N_STATS - 1) % N_STATS]; + stats->last_stats = frame_time; + } + else + { + *suggested_change = 0; + } + + stats->last_frame = frame_time; + stats->frame_counter[stats->stats_index]++; + stats->frame_counter_max = MAX (stats->frame_counter_max, stats->frame_counter[stats->stats_index]); + + return elapsed; +} + +static void +stats_update (GtkWidget *widget, + gint n_items) +{ + Stats *stats; + + stats = get_stats (widget); + + g_assert ((gint) stats->item_counter[stats->stats_index] + n_items > 0); + stats->item_counter[stats->stats_index] += n_items; +} + +typedef struct _FishData FishData; +struct _FishData { + double x; + double y; + double x_speed; + double y_speed; +}; + +static FishData * +get_fish_data (GtkWidget *fish) +{ + static GQuark fish_quark = 0; + FishData *data; + + if (G_UNLIKELY (fish_quark == 0)) + fish_quark = g_quark_from_static_string ("fish"); + + data = g_object_get_qdata (G_OBJECT (fish), fish_quark); + if (data == NULL) + { + data = g_new0 (FishData, 1); + g_object_set_qdata_full (G_OBJECT (fish), fish_quark, data, g_free); + data->x = 10; + data->y = 10; + data->x_speed = g_random_double_range (1, 200); + data->y_speed = g_random_double_range (1, 200); + } + + return data; +} + +static void +add_fish (GtkWidget *bowl, + guint n_fish) +{ + GtkWidget *new_fish; + guint i; + + for (i = 0; i < n_fish; i++) + { + new_fish = gtk_image_new_from_icon_name (get_random_icon_name (gtk_icon_theme_get_default ()), + GTK_ICON_SIZE_DIALOG); + gtk_widget_show (new_fish); + + gtk_fixed_put (GTK_FIXED (bowl), + new_fish, + 10, 10); + } + + stats_update (bowl, n_fish); +} + +static void +remove_fish (GtkWidget *bowl, + guint n_fish) +{ + GList *list, *children; + guint i; + + children = gtk_container_get_children (GTK_CONTAINER (bowl)); + g_assert (n_fish < g_list_length (children)); + + list = children; + for (i = 0; i < n_fish; i++) + { + gtk_container_remove (GTK_CONTAINER (bowl), list->data); + list = list->next; + } + + g_list_free (children); + + stats_update (bowl, - (gint) n_fish); + + { + Stats *stats = get_stats (bowl); + + children = gtk_container_get_children (GTK_CONTAINER (bowl)); + g_assert (stats->item_counter[stats->stats_index] == g_list_length (children)); + g_list_free (children); + } +} + +static void +move_one_fish (GtkWidget *fish, + gpointer elapsedp) +{ + GtkWidget *fixed = gtk_widget_get_parent (fish); + FishData *data = get_fish_data (fish); + gint64 elapsed = *(gint64 *) elapsedp; + + data->x += data->x_speed * ((double) elapsed / G_USEC_PER_SEC); + data->y += data->y_speed * ((double) elapsed / G_USEC_PER_SEC); + + if (data->x <= 0) + { + data->x = 0; + data->x_speed = - g_random_double_range (1, 200) * (data->x_speed > 0 ? 1 : -1); + } + else if (data->x > gtk_widget_get_allocated_width (fixed) - gtk_widget_get_allocated_width (fish)) + { + data->x = gtk_widget_get_allocated_width (fixed) - gtk_widget_get_allocated_width (fish); + data->x_speed = - g_random_double_range (1, 200) * (data->x_speed > 0 ? 1 : -1); + } + + if (data->y <= 0) + { + data->y = 0; + data->y_speed = - g_random_double_range (1, 200) * (data->y_speed > 0 ? 1 : -1); + } + else if (data->y > gtk_widget_get_allocated_height (fixed) - gtk_widget_get_allocated_height (fish)) + { + data->y = gtk_widget_get_allocated_height (fixed) - gtk_widget_get_allocated_height (fish); + data->y_speed = - g_random_double_range (1, 200) * (data->y_speed > 0 ? 1 : -1); + } + + gtk_fixed_move (GTK_FIXED (fixed), fish, data->x, data->y); +} + +static gboolean +move_fish (GtkWidget *bowl, + GdkFrameClock *frame_clock, + gpointer info_label) +{ + gint64 elapsed; + gint suggested_change; + + elapsed = do_stats (bowl, info_label, &suggested_change); + + gtk_container_foreach (GTK_CONTAINER (bowl), move_one_fish, &elapsed); + + if (suggested_change > 0) + add_fish (bowl, suggested_change); + else if (suggested_change < 0) + remove_fish (bowl, - suggested_change); + + return G_SOURCE_CONTINUE; +} + +GtkWidget * +do_fishbowl (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkBuilder *builder; + GtkWidget *bowl, *info_label; + + builder = gtk_builder_new_from_resource ("/fishbowl/fishbowl.ui"); + gtk_builder_connect_signals (builder, NULL); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); + bowl = GTK_WIDGET (gtk_builder_get_object (builder, "bowl")); + info_label = GTK_WIDGET (gtk_builder_get_object (builder, "info_label")); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + gtk_widget_realize (window); + gtk_widget_add_tick_callback (bowl, move_fish, info_label, NULL); + + //add_fish (bowl, 300); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + + return window; +} diff --git a/demos/gtk-demo/fishbowl.ui b/demos/gtk-demo/fishbowl.ui new file mode 100644 index 0000000000..e46851b17d --- /dev/null +++ b/demos/gtk-demo/fishbowl.ui @@ -0,0 +1,27 @@ + + + + + Fishbowl + + + True + True + + + True + 0 fish - 0 fps + + + end + + + + + + + True + + + +