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