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 "frame-stats.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 double load_factor = 1.0; |
23 | static double cb_no_resize = FALSE; |
24 | |
25 | static cairo_surface_t *source_surface; |
26 | |
27 | static void |
28 | ensure_resources(cairo_surface_t *target) |
29 | { |
30 | cairo_t *cr; |
31 | int i, j; |
32 | |
33 | if (source_surface != NULL) |
34 | return; |
35 | |
36 | source_surface = cairo_surface_create_similar (other: target, content: CAIRO_CONTENT_COLOR_ALPHA, |
37 | width: 16 * DIAMETER, height: 16 * DIAMETER); |
38 | cr = cairo_create(target: source_surface); |
39 | |
40 | cairo_save(cr); |
41 | cairo_set_source_rgba(cr, red: 0, green: 0, blue: 0, alpha: 0); |
42 | cairo_set_operator(cr, op: CAIRO_OPERATOR_SOURCE); |
43 | cairo_paint(cr); |
44 | cairo_restore(cr); |
45 | |
46 | cairo_set_line_width(cr, width: 1.0); |
47 | |
48 | for (j = 0; j < 16; j++) |
49 | for (i = 0; i < 16; i++) |
50 | { |
51 | cairo_set_source_rgba(cr, |
52 | red: ((i * 41) % 16) / 15., |
53 | green: ((i * 31) % 16) / 15., |
54 | blue: ((i * 23) % 16) / 15., |
55 | alpha: 0.25); |
56 | cairo_arc(cr, |
57 | xc: i * DIAMETER + RADIUS, yc: j * DIAMETER + RADIUS, |
58 | RADIUS - 0.5, angle1: 0, angle2: 2 * M_PI); |
59 | cairo_fill_preserve(cr); |
60 | cairo_set_source_rgba(cr, |
61 | red: ((i * 41) % 16) / 15., |
62 | green: ((i * 31) % 16) / 15., |
63 | blue: ((i * 23) % 16) / 15., |
64 | alpha: 1.0); |
65 | cairo_stroke(cr); |
66 | } |
67 | } |
68 | |
69 | static void |
70 | on_draw (GtkDrawingArea *da, |
71 | cairo_t *cr, |
72 | int width, |
73 | int height, |
74 | gpointer data) |
75 | |
76 | { |
77 | GRand *rand = g_rand_new_with_seed(seed: 0); |
78 | int i; |
79 | |
80 | ensure_resources (target: cairo_get_target (cr)); |
81 | |
82 | cairo_set_source_rgb(cr, red: 1, green: 1, blue: 1); |
83 | cairo_paint(cr); |
84 | |
85 | cairo_set_source_rgb(cr, red: 0, green: 0, blue: 0); |
86 | cairo_set_line_width(cr, width: 1.0); |
87 | cairo_rectangle (cr, x: 0.5, y: 0.5, width: width - 1, height: height - 1); |
88 | cairo_stroke (cr); |
89 | |
90 | for(i = 0; i < load_factor * 150; i++) |
91 | { |
92 | int source = g_rand_int_range(rand_: rand, begin: 0, end: 255); |
93 | double phi = g_rand_double_range(rand_: rand, begin: 0, end: 2 * M_PI) + angle; |
94 | double r = g_rand_double_range(rand_: rand, begin: 0, end: width / 2 - RADIUS); |
95 | int x, y; |
96 | |
97 | int source_x = (source % 16) * DIAMETER; |
98 | int source_y = (source / 16) * DIAMETER; |
99 | |
100 | x = round(x: width / 2 + r * cos(x: phi) - RADIUS); |
101 | y = round(x: height / 2 - r * sin(x: phi) - RADIUS); |
102 | |
103 | cairo_set_source_surface(cr, surface: source_surface, |
104 | x: x - source_x, y: y - source_y); |
105 | cairo_rectangle(cr, x, y, DIAMETER, DIAMETER); |
106 | cairo_fill(cr); |
107 | } |
108 | |
109 | g_rand_free(rand_: rand); |
110 | } |
111 | |
112 | static void |
113 | on_frame (double progress) |
114 | { |
115 | int jitter; |
116 | |
117 | angle = 2 * M_PI * progress; |
118 | jitter = WINDOW_SIZE_JITTER * sin(x: angle); |
119 | |
120 | if (!cb_no_resize) |
121 | { |
122 | window_width = WIDTH + jitter; |
123 | window_height = HEIGHT + jitter; |
124 | } |
125 | |
126 | gtk_window_set_default_size (GTK_WINDOW (window), |
127 | width: window_width, height: window_height); |
128 | |
129 | gtk_widget_queue_draw (widget: window); |
130 | } |
131 | |
132 | static gboolean |
133 | resize_idle (gpointer user_data) |
134 | { |
135 | GdkFrameClock *frame_clock = user_data; |
136 | gint64 frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
137 | double scaled_time; |
138 | |
139 | if (start_frame_time == 0) |
140 | start_frame_time = frame_time; |
141 | |
142 | scaled_time = (frame_time - start_frame_time) / (CYCLE_TIME * 1000000); |
143 | on_frame (progress: scaled_time - floor (x: scaled_time)); |
144 | |
145 | return G_SOURCE_REMOVE; |
146 | } |
147 | |
148 | static gboolean |
149 | tick_callback (GtkWidget *widget, |
150 | GdkFrameClock *frame_clock, |
151 | gpointer user_data) |
152 | { |
153 | g_idle_add (function: resize_idle, data: frame_clock); |
154 | |
155 | return G_SOURCE_CONTINUE; |
156 | } |
157 | |
158 | static gboolean |
159 | on_map (GtkWidget *widget) |
160 | { |
161 | gtk_widget_add_tick_callback (widget: window, callback: tick_callback, NULL, NULL); |
162 | |
163 | return FALSE; |
164 | } |
165 | |
166 | static GOptionEntry options[] = { |
167 | { "factor" , 'f', 0, G_OPTION_ARG_DOUBLE, &load_factor, "Load factor" , "FACTOR" }, |
168 | { "no-resize" , 'n', 0, G_OPTION_ARG_NONE, &cb_no_resize, "No Resize" , NULL }, |
169 | { NULL } |
170 | }; |
171 | |
172 | static void |
173 | quit_cb (GtkWidget *widget, |
174 | gpointer data) |
175 | { |
176 | gboolean *done = data; |
177 | |
178 | *done = TRUE; |
179 | |
180 | g_main_context_wakeup (NULL); |
181 | } |
182 | |
183 | int |
184 | main(int argc, char **argv) |
185 | { |
186 | GError *error = NULL; |
187 | GtkWidget *da; |
188 | gboolean done = FALSE; |
189 | |
190 | GOptionContext *context = g_option_context_new (NULL); |
191 | g_option_context_add_main_entries (context, entries: options, NULL); |
192 | frame_stats_add_options (group: g_option_context_get_main_group (context)); |
193 | |
194 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
195 | { |
196 | g_printerr (format: "Option parsing failed: %s\n" , error->message); |
197 | return 1; |
198 | } |
199 | |
200 | gtk_init (); |
201 | |
202 | g_print (format: "# Load factor: %g\n" , |
203 | load_factor); |
204 | g_print (format: "# Resizing?: %s\n" , |
205 | cb_no_resize ? "no" : "yes" ); |
206 | |
207 | window = gtk_window_new (); |
208 | frame_stats_ensure (GTK_WINDOW (window)); |
209 | |
210 | da = gtk_drawing_area_new (); |
211 | gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func: on_draw, NULL, NULL); |
212 | gtk_window_set_child (GTK_WINDOW (window), child: da); |
213 | |
214 | g_signal_connect (window, "destroy" , |
215 | G_CALLBACK (quit_cb), NULL); |
216 | |
217 | g_signal_connect (window, "map" , |
218 | G_CALLBACK (on_map), NULL); |
219 | on_frame (progress: 0.); |
220 | |
221 | gtk_widget_show (widget: window); |
222 | |
223 | while (!done) |
224 | g_main_context_iteration (NULL, TRUE); |
225 | |
226 | return 0; |
227 | } |
228 | |