]> Pileus Git - ~andy/gtk/blob - tests/animated-resizing.c
f2bbc1013c9a43378e57ce080f5a0dd86cdcbe13
[~andy/gtk] / tests / animated-resizing.c
1 /* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2
3 #include <gtk/gtk.h>
4 #include <math.h>
5 #include <string.h>
6
7 #include "variable.h"
8
9 #define RADIUS 64
10 #define DIAMETER (2*RADIUS)
11 #define WIDTH 600
12 #define HEIGHT 600
13 #define WINDOW_SIZE_JITTER 200
14 #define CYCLE_TIME 5.
15
16 static GtkWidget *window;
17 static int window_width = WIDTH, window_height = HEIGHT;
18
19 static double angle;
20
21 static int max_stats = -1;
22 static double statistics_time = 5.;
23 static double load_factor = 1.0;
24 static double cb_no_resize = FALSE;
25 static gboolean machine_readable = FALSE;
26
27 static cairo_surface_t *source_surface;
28
29 static void
30 ensure_resources(cairo_surface_t *target)
31 {
32   cairo_t *cr;
33   int i, j;
34
35   if (source_surface != NULL)
36     return;
37
38   source_surface = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR_ALPHA,
39                                                  16 * DIAMETER, 16 * DIAMETER);
40   cr = cairo_create(source_surface);
41
42   cairo_save(cr);
43   cairo_set_source_rgba(cr, 0, 0, 0, 0);
44   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
45   cairo_paint(cr);
46   cairo_restore(cr);
47
48   cairo_set_line_width(cr, 1.0);
49
50   for (j = 0; j < 16; j++)
51     for (i = 0; i < 16; i++)
52       {
53         cairo_set_source_rgba(cr,
54                               ((i * 41) % 16) / 15.,
55                               ((i * 31) % 16) / 15.,
56                               ((i * 23) % 16) / 15.,
57                               0.25);
58         cairo_arc(cr,
59                   i * DIAMETER + RADIUS, j * DIAMETER + RADIUS,
60                   RADIUS - 0.5, 0, 2 * M_PI);
61         cairo_fill_preserve(cr);
62         cairo_set_source_rgba(cr,
63                               ((i * 41) % 16) / 15.,
64                               ((i * 31) % 16) / 15.,
65                               ((i * 23) % 16) / 15.,
66                               1.0);
67         cairo_stroke(cr);
68       }
69 }
70
71 static void
72 on_window_draw (GtkWidget *widget,
73                 cairo_t   *cr)
74
75 {
76   GRand *rand = g_rand_new_with_seed(0);
77   int i;
78   int width, height;
79
80   width = gtk_widget_get_allocated_width (widget);
81   height = gtk_widget_get_allocated_height (widget);
82
83   ensure_resources (cairo_get_target (cr));
84
85   cairo_set_source_rgb(cr, 1, 1, 1);
86   cairo_paint(cr);
87
88   cairo_set_source_rgb(cr, 0, 0, 0);
89   cairo_set_line_width(cr, 1.0);
90   cairo_rectangle (cr, 0.5, 0.5, width - 1, height - 1);
91   cairo_stroke (cr);
92
93   for(i = 0; i < load_factor * 150; i++)
94     {
95       int source = g_rand_int_range(rand, 0, 255);
96       double phi = g_rand_double_range(rand, 0, 2 * M_PI) + angle;
97       double r = g_rand_double_range(rand, 0, width / 2 - RADIUS);
98       int x, y;
99
100       int source_x = (source % 16) * DIAMETER;
101       int source_y = (source / 16) * DIAMETER;
102
103       x = round(width / 2 + r * cos(phi) - RADIUS);
104       y = round(height / 2 - r * sin(phi) - RADIUS);
105
106       cairo_set_source_surface(cr, source_surface,
107                                x - source_x, y - source_y);
108       cairo_rectangle(cr, x, y, DIAMETER, DIAMETER);
109       cairo_fill(cr);
110     }
111
112   g_rand_free(rand);
113 }
114
115 static void
116 print_double (const char *description,
117               double      value)
118 {
119   if (machine_readable)
120     g_print ("%g\t", value);
121   else
122     g_print ("%s: %g\n", description, value);
123 }
124
125 static void
126 print_variable (const char *description,
127                 Variable *variable)
128 {
129   if (variable->weight != 0)
130     {
131       if (machine_readable)
132         g_print ("%g\t%g\t",
133                  variable_mean (variable),
134                  variable_standard_deviation (variable));
135       else
136         g_print ("%s: %g +/- %g\n", description,
137                  variable_mean (variable),
138                  variable_standard_deviation (variable));
139     }
140   else
141     {
142       if (machine_readable)
143         g_print ("-\t-\t");
144       else
145         g_print ("%s: <n/a>\n", description);
146     }
147 }
148
149 static void
150 handle_frame_stats (GdkFrameHistory *frame_history)
151 {
152   static int num_stats = 0;
153   static double last_print_time = 0;
154   static int frames_since_last_print = 0;
155   static gint64 frame_counter;
156   static gint64 last_handled_frame = -1;
157
158   static Variable latency = VARIABLE_INIT;
159
160   double current_time;
161
162   current_time = g_get_monotonic_time ();
163   if (current_time >= last_print_time + 1000000 * statistics_time)
164     {
165       if (frames_since_last_print)
166         {
167           if (num_stats == 0 && machine_readable)
168             {
169               g_print ("# load_factor frame_rate latency\n");
170             }
171
172           num_stats++;
173           if (machine_readable)
174             g_print ("%g        ", load_factor);
175           print_double ("Frame rate ",
176                         frames_since_last_print / ((current_time - last_print_time) / 1000000.));
177
178           print_variable ("Latency", &latency);
179
180           g_print ("\n");
181         }
182
183       last_print_time = current_time;
184       frames_since_last_print = 0;
185       variable_reset (&latency);
186
187       if (num_stats == max_stats)
188         gtk_main_quit ();
189     }
190
191   frames_since_last_print++;
192
193   for (frame_counter = last_handled_frame;
194        frame_counter < gdk_frame_history_get_frame_counter (frame_history);
195        frame_counter++)
196     {
197       GdkFrameTimings *timings = gdk_frame_history_get_timings (frame_history, frame_counter);
198       GdkFrameTimings *previous_timings = gdk_frame_history_get_timings (frame_history, frame_counter - 1);
199
200       if (!timings || gdk_frame_timings_get_complete (timings))
201         last_handled_frame = frame_counter;
202
203       if (timings && gdk_frame_timings_get_complete (timings) && previous_timings &&
204           gdk_frame_timings_get_presentation_time (timings) != 0 &&
205           gdk_frame_timings_get_presentation_time (previous_timings) != 0)
206         {
207           double display_time = (gdk_frame_timings_get_presentation_time (timings) - gdk_frame_timings_get_presentation_time (previous_timings)) / 1000.;
208           double frame_latency = (gdk_frame_timings_get_presentation_time (previous_timings) - gdk_frame_timings_get_frame_time (previous_timings)) / 1000. + display_time / 2;
209
210           variable_add_weighted (&latency, frame_latency, display_time);
211         }
212     }
213 }
214
215 static void
216 on_frame (GtkTimeline *timeline,
217           double       progress)
218 {
219   GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (window);
220   int jitter;
221
222   if (frame_clock)
223     {
224       GdkFrameHistory *history = gdk_frame_clock_get_history (frame_clock);
225       handle_frame_stats (history);
226     }
227
228   angle = 2 * M_PI * progress;
229   jitter = WINDOW_SIZE_JITTER * sin(angle);
230
231   if (!cb_no_resize)
232     {
233       window_width = WIDTH + jitter;
234       window_height = HEIGHT + jitter;
235     }
236
237   gtk_window_resize (GTK_WINDOW (window),
238                      window_width, window_height);
239
240   gtk_widget_queue_draw (window);
241 }
242
243 static gboolean
244 on_map_event (GtkWidget   *widget,
245               GdkEventAny *event,
246               GtkTimeline *timeline)
247 {
248   gtk_timeline_start (timeline);
249
250   return FALSE;
251 }
252
253 static GOptionEntry options[] = {
254   { "factor", 'f', 0, G_OPTION_ARG_DOUBLE, &load_factor, "Load factor", "FACTOR" },
255   { "max-statistics", 'm', 0, G_OPTION_ARG_INT, &max_stats, "Maximum statistics printed", NULL },
256   { "machine-readable", 0, 0, G_OPTION_ARG_NONE, &machine_readable, "Print statistics in columns", NULL },
257   { "no-resize", 'n', 0, G_OPTION_ARG_NONE, &cb_no_resize, "No Resize", NULL },
258   { "statistics-time", 's', 0, G_OPTION_ARG_DOUBLE, &statistics_time, "Statistics accumulation time", "TIME" },
259   { NULL }
260 };
261
262 int
263 main(int argc, char **argv)
264 {
265   GError *error = NULL;
266   GdkScreen *screen;
267   GdkRectangle monitor_bounds;
268   GtkTimeline *timeline;
269
270   if (!gtk_init_with_args (&argc, &argv, "",
271                            options, NULL, &error))
272     {
273       g_printerr ("Option parsing failed: %s\n", error->message);
274       return 1;
275     }
276
277   g_print ("%sLoad factor: %g\n",
278            machine_readable ? "# " : "",
279            load_factor);
280   g_print ("%sResizing?: %s\n",
281            machine_readable ? "# " : "",
282            cb_no_resize ? "no" : "yes");
283
284   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
285   gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
286   gtk_window_set_gravity (GTK_WINDOW (window), GDK_GRAVITY_CENTER);
287   gtk_widget_set_app_paintable (window, TRUE);
288
289   g_signal_connect (window, "draw",
290                     G_CALLBACK (on_window_draw), NULL);
291   g_signal_connect (window, "destroy",
292                     G_CALLBACK (gtk_main_quit), NULL);
293
294   timeline = gtk_timeline_new (window, CYCLE_TIME * 1000);
295   gtk_timeline_set_loop (timeline, TRUE);
296   gtk_timeline_set_progress_type (timeline, GTK_TIMELINE_PROGRESS_LINEAR);
297
298   g_signal_connect (timeline, "frame",
299                     G_CALLBACK (on_frame), NULL);
300   g_signal_connect (window, "map-event",
301                     G_CALLBACK (on_map_event), timeline);
302   on_frame (timeline, 0.);
303
304   screen = gtk_widget_get_screen (window);
305   gdk_screen_get_monitor_geometry (screen,
306                                    gdk_screen_get_primary_monitor (screen),
307                                    &monitor_bounds);
308
309   gtk_window_move (GTK_WINDOW (window),
310                    monitor_bounds.x + (monitor_bounds.width - window_width) / 2,
311                    monitor_bounds.y + (monitor_bounds.height - window_height) / 2);
312
313   gtk_widget_show (window);
314
315   gtk_main ();
316
317   return 0;
318 }