1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2017 Benjamin Otte <otte@gnome.org> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "gtkfishbowl.h" |
21 | |
22 | typedef struct _GtkFishbowlPrivate GtkFishbowlPrivate; |
23 | typedef struct _GtkFishbowlChild GtkFishbowlChild; |
24 | |
25 | struct _GtkFishbowlPrivate |
26 | { |
27 | GtkFishCreationFunc creation_func; |
28 | GHashTable *children; |
29 | guint count; |
30 | |
31 | gint64 last_frame_time; |
32 | gint64 update_delay; |
33 | guint tick_id; |
34 | |
35 | double framerate; |
36 | int last_benchmark_change; |
37 | |
38 | guint benchmark : 1; |
39 | }; |
40 | |
41 | struct _GtkFishbowlChild |
42 | { |
43 | GtkWidget *widget; |
44 | double x; |
45 | double y; |
46 | double dx; |
47 | double dy; |
48 | }; |
49 | |
50 | enum { |
51 | PROP_0, |
52 | PROP_ANIMATING, |
53 | PROP_BENCHMARK, |
54 | PROP_COUNT, |
55 | PROP_FRAMERATE, |
56 | PROP_UPDATE_DELAY, |
57 | NUM_PROPERTIES |
58 | }; |
59 | |
60 | static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; |
61 | |
62 | G_DEFINE_TYPE_WITH_PRIVATE (GtkFishbowl, gtk_fishbowl, GTK_TYPE_WIDGET) |
63 | |
64 | static void |
65 | gtk_fishbowl_init (GtkFishbowl *fishbowl) |
66 | { |
67 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
68 | |
69 | priv->update_delay = G_USEC_PER_SEC; |
70 | priv->children = g_hash_table_new_full (NULL, NULL, |
71 | NULL, value_destroy_func: (GDestroyNotify) g_free); |
72 | } |
73 | |
74 | /** |
75 | * gtk_fishbowl_new: |
76 | * |
77 | * Creates a new `GtkFishbowl`. |
78 | * |
79 | * Returns: a new `GtkFishbowl`. |
80 | */ |
81 | GtkWidget* |
82 | gtk_fishbowl_new (void) |
83 | { |
84 | return g_object_new (GTK_TYPE_FISHBOWL, NULL); |
85 | } |
86 | |
87 | static void |
88 | gtk_fishbowl_measure (GtkWidget *widget, |
89 | GtkOrientation orientation, |
90 | int for_size, |
91 | int *minimum, |
92 | int *natural, |
93 | int *minimum_baseline, |
94 | int *natural_baseline) |
95 | { |
96 | GtkFishbowl *fishbowl = GTK_FISHBOWL (widget); |
97 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
98 | GHashTableIter iter; |
99 | gpointer key, value; |
100 | GtkFishbowlChild *child; |
101 | int child_min, child_nat; |
102 | |
103 | *minimum = 0; |
104 | *natural = 0; |
105 | |
106 | g_hash_table_iter_init (iter: &iter, hash_table: priv->children); |
107 | while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value)) |
108 | { |
109 | child = value; |
110 | |
111 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
112 | { |
113 | gtk_widget_measure (widget: child->widget, orientation, for_size: -1, minimum: &child_min, natural: &child_nat, NULL, NULL); |
114 | } |
115 | else |
116 | { |
117 | int min_width; |
118 | |
119 | gtk_widget_measure (widget: child->widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, minimum: &min_width, NULL, NULL, NULL); |
120 | gtk_widget_measure (widget: child->widget, orientation, for_size: min_width, minimum: &child_min, natural: &child_nat, NULL, NULL); |
121 | } |
122 | |
123 | *minimum = MAX (*minimum, child_min); |
124 | *natural = MAX (*natural, child_nat); |
125 | } |
126 | } |
127 | |
128 | static void |
129 | gtk_fishbowl_size_allocate (GtkWidget *widget, |
130 | int width, |
131 | int height, |
132 | int baseline) |
133 | { |
134 | GtkFishbowl *fishbowl = GTK_FISHBOWL (widget); |
135 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
136 | GtkFishbowlChild *child; |
137 | GtkAllocation child_allocation; |
138 | GtkRequisition child_requisition; |
139 | GHashTableIter iter; |
140 | gpointer key, value; |
141 | |
142 | g_hash_table_iter_init (iter: &iter, hash_table: priv->children); |
143 | while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value)) |
144 | { |
145 | child = value; |
146 | |
147 | gtk_widget_get_preferred_size (widget: child->widget, minimum_size: &child_requisition, NULL); |
148 | child_allocation.x = round (x: child->x * (width - child_requisition.width)); |
149 | child_allocation.y = round (x: child->y * (height - child_requisition.height)); |
150 | child_allocation.width = child_requisition.width; |
151 | child_allocation.height = child_requisition.height; |
152 | |
153 | gtk_widget_size_allocate (widget: child->widget, allocation: &child_allocation, baseline: -1); |
154 | } |
155 | } |
156 | |
157 | static double |
158 | new_speed (void) |
159 | { |
160 | /* 5s to 50s to cross screen seems fair */ |
161 | return g_random_double_range (begin: 0.02, end: 0.2); |
162 | } |
163 | |
164 | static void |
165 | gtk_fishbowl_add (GtkFishbowl *fishbowl, |
166 | GtkWidget *widget) |
167 | { |
168 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
169 | GtkFishbowlChild *child_info; |
170 | |
171 | g_return_if_fail (GTK_IS_FISHBOWL (fishbowl)); |
172 | g_return_if_fail (GTK_IS_WIDGET (widget)); |
173 | |
174 | child_info = g_new0 (GtkFishbowlChild, 1); |
175 | child_info->widget = widget; |
176 | child_info->x = 0; |
177 | child_info->y = 0; |
178 | child_info->dx = new_speed (); |
179 | child_info->dy = new_speed (); |
180 | |
181 | gtk_widget_set_parent (widget, GTK_WIDGET (fishbowl)); |
182 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: widget), |
183 | first_state: GTK_ACCESSIBLE_STATE_HIDDEN, TRUE, |
184 | -1); |
185 | |
186 | g_hash_table_insert (hash_table: priv->children, key: widget, value: child_info); |
187 | priv->count++; |
188 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_COUNT]); |
189 | } |
190 | |
191 | static void |
192 | gtk_fishbowl_remove (GtkFishbowl *fishbowl, |
193 | GtkWidget *widget) |
194 | { |
195 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
196 | |
197 | if (g_hash_table_remove (hash_table: priv->children, key: widget)) |
198 | { |
199 | gtk_widget_unparent (widget); |
200 | |
201 | priv->count--; |
202 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_COUNT]); |
203 | } |
204 | } |
205 | |
206 | static void |
207 | gtk_fishbowl_finalize (GObject *object) |
208 | { |
209 | GtkFishbowl *fishbowl = GTK_FISHBOWL (object); |
210 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
211 | |
212 | g_hash_table_destroy (hash_table: priv->children); |
213 | priv->children = NULL; |
214 | } |
215 | |
216 | static void |
217 | gtk_fishbowl_dispose (GObject *object) |
218 | { |
219 | GtkFishbowl *fishbowl = GTK_FISHBOWL (object); |
220 | |
221 | gtk_fishbowl_set_animating (fishbowl, FALSE); |
222 | gtk_fishbowl_set_count (fishbowl, count: 0); |
223 | |
224 | G_OBJECT_CLASS (gtk_fishbowl_parent_class)->dispose (object); |
225 | } |
226 | |
227 | static void |
228 | gtk_fishbowl_set_property (GObject *object, |
229 | guint prop_id, |
230 | const GValue *value, |
231 | GParamSpec *pspec) |
232 | { |
233 | GtkFishbowl *fishbowl = GTK_FISHBOWL (object); |
234 | |
235 | switch (prop_id) |
236 | { |
237 | case PROP_ANIMATING: |
238 | gtk_fishbowl_set_animating (fishbowl, animating: g_value_get_boolean (value)); |
239 | break; |
240 | |
241 | case PROP_BENCHMARK: |
242 | gtk_fishbowl_set_benchmark (fishbowl, animating: g_value_get_boolean (value)); |
243 | break; |
244 | |
245 | case PROP_COUNT: |
246 | gtk_fishbowl_set_count (fishbowl, count: g_value_get_uint (value)); |
247 | break; |
248 | |
249 | case PROP_UPDATE_DELAY: |
250 | gtk_fishbowl_set_update_delay (fishbowl, update_delay: g_value_get_int64 (value)); |
251 | break; |
252 | |
253 | default: |
254 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
255 | break; |
256 | } |
257 | } |
258 | |
259 | static void |
260 | gtk_fishbowl_get_property (GObject *object, |
261 | guint prop_id, |
262 | GValue *value, |
263 | GParamSpec *pspec) |
264 | { |
265 | GtkFishbowl *fishbowl = GTK_FISHBOWL (object); |
266 | |
267 | switch (prop_id) |
268 | { |
269 | case PROP_ANIMATING: |
270 | g_value_set_boolean (value, v_boolean: gtk_fishbowl_get_animating (fishbowl)); |
271 | break; |
272 | |
273 | case PROP_BENCHMARK: |
274 | g_value_set_boolean (value, v_boolean: gtk_fishbowl_get_benchmark (fishbowl)); |
275 | break; |
276 | |
277 | case PROP_COUNT: |
278 | g_value_set_uint (value, v_uint: gtk_fishbowl_get_count (fishbowl)); |
279 | break; |
280 | |
281 | case PROP_FRAMERATE: |
282 | g_value_set_double (value, v_double: gtk_fishbowl_get_framerate (fishbowl)); |
283 | break; |
284 | |
285 | case PROP_UPDATE_DELAY: |
286 | g_value_set_int64 (value, v_int64: gtk_fishbowl_get_update_delay (fishbowl)); |
287 | break; |
288 | |
289 | default: |
290 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
291 | break; |
292 | } |
293 | } |
294 | |
295 | static void |
296 | gtk_fishbowl_class_init (GtkFishbowlClass *klass) |
297 | { |
298 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
299 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
300 | |
301 | object_class->finalize = gtk_fishbowl_finalize; |
302 | object_class->dispose = gtk_fishbowl_dispose; |
303 | object_class->set_property = gtk_fishbowl_set_property; |
304 | object_class->get_property = gtk_fishbowl_get_property; |
305 | |
306 | widget_class->measure = gtk_fishbowl_measure; |
307 | widget_class->size_allocate = gtk_fishbowl_size_allocate; |
308 | |
309 | props[PROP_ANIMATING] = |
310 | g_param_spec_boolean (name: "animating" , |
311 | nick: "animating" , |
312 | blurb: "Whether children are moving around" , |
313 | FALSE, |
314 | flags: G_PARAM_READWRITE); |
315 | |
316 | props[PROP_BENCHMARK] = |
317 | g_param_spec_boolean (name: "benchmark" , |
318 | nick: "Benchmark" , |
319 | blurb: "Adapt the count property to hit the maximum framerate" , |
320 | FALSE, |
321 | flags: G_PARAM_READWRITE); |
322 | |
323 | props[PROP_COUNT] = |
324 | g_param_spec_uint (name: "count" , |
325 | nick: "Count" , |
326 | blurb: "Number of widgets" , |
327 | minimum: 0, G_MAXUINT, |
328 | default_value: 0, |
329 | flags: G_PARAM_READWRITE); |
330 | |
331 | props[PROP_FRAMERATE] = |
332 | g_param_spec_double (name: "framerate" , |
333 | nick: "Framerate" , |
334 | blurb: "Framerate of this widget in frames per second" , |
335 | minimum: 0, G_MAXDOUBLE, |
336 | default_value: 0, |
337 | flags: G_PARAM_READABLE); |
338 | |
339 | props[PROP_UPDATE_DELAY] = |
340 | g_param_spec_int64 (name: "update-delay" , |
341 | nick: "Update delay" , |
342 | blurb: "Number of usecs between updates" , |
343 | minimum: 0, G_MAXINT64, |
344 | G_USEC_PER_SEC, |
345 | flags: G_PARAM_READWRITE); |
346 | |
347 | g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: props); |
348 | |
349 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_PRESENTATION); |
350 | } |
351 | |
352 | guint |
353 | gtk_fishbowl_get_count (GtkFishbowl *fishbowl) |
354 | { |
355 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
356 | |
357 | return priv->count; |
358 | } |
359 | |
360 | void |
361 | gtk_fishbowl_set_count (GtkFishbowl *fishbowl, |
362 | guint count) |
363 | { |
364 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
365 | |
366 | if (priv->count == count) |
367 | return; |
368 | |
369 | g_object_freeze_notify (G_OBJECT (fishbowl)); |
370 | |
371 | while (priv->count > count) |
372 | { |
373 | gtk_fishbowl_remove (fishbowl, widget: gtk_widget_get_first_child (GTK_WIDGET (fishbowl))); |
374 | } |
375 | |
376 | while (priv->count < count) |
377 | { |
378 | GtkWidget *new_widget; |
379 | |
380 | new_widget = priv->creation_func (); |
381 | |
382 | gtk_fishbowl_add (fishbowl, widget: new_widget); |
383 | } |
384 | |
385 | g_object_thaw_notify (G_OBJECT (fishbowl)); |
386 | } |
387 | |
388 | gboolean |
389 | gtk_fishbowl_get_benchmark (GtkFishbowl *fishbowl) |
390 | { |
391 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
392 | |
393 | return priv->benchmark; |
394 | } |
395 | |
396 | void |
397 | gtk_fishbowl_set_benchmark (GtkFishbowl *fishbowl, |
398 | gboolean benchmark) |
399 | { |
400 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
401 | |
402 | if (priv->benchmark == benchmark) |
403 | return; |
404 | |
405 | priv->benchmark = benchmark; |
406 | if (!benchmark) |
407 | priv->last_benchmark_change = 0; |
408 | |
409 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_BENCHMARK]); |
410 | } |
411 | |
412 | gboolean |
413 | gtk_fishbowl_get_animating (GtkFishbowl *fishbowl) |
414 | { |
415 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
416 | |
417 | return priv->tick_id != 0; |
418 | } |
419 | |
420 | static gint64 |
421 | guess_refresh_interval (GdkFrameClock *frame_clock) |
422 | { |
423 | gint64 interval; |
424 | gint64 i; |
425 | |
426 | interval = G_MAXINT64; |
427 | |
428 | for (i = gdk_frame_clock_get_history_start (frame_clock); |
429 | i < gdk_frame_clock_get_frame_counter (frame_clock); |
430 | i++) |
431 | { |
432 | GdkFrameTimings *t, *before; |
433 | gint64 ts, before_ts; |
434 | |
435 | t = gdk_frame_clock_get_timings (frame_clock, frame_counter: i); |
436 | before = gdk_frame_clock_get_timings (frame_clock, frame_counter: i - 1); |
437 | if (t == NULL || before == NULL) |
438 | continue; |
439 | |
440 | ts = gdk_frame_timings_get_frame_time (timings: t); |
441 | before_ts = gdk_frame_timings_get_frame_time (timings: before); |
442 | if (ts == 0 || before_ts == 0) |
443 | continue; |
444 | |
445 | interval = MIN (interval, ts - before_ts); |
446 | } |
447 | |
448 | if (interval == G_MAXINT64) |
449 | return 0; |
450 | |
451 | return interval; |
452 | } |
453 | |
454 | static void |
455 | gtk_fishbowl_do_update (GtkFishbowl *fishbowl) |
456 | { |
457 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
458 | GdkFrameClock *frame_clock; |
459 | GdkFrameTimings *start, *end; |
460 | gint64 start_counter, end_counter; |
461 | gint64 n_frames, expected_frames; |
462 | gint64 start_timestamp, end_timestamp; |
463 | gint64 interval; |
464 | |
465 | frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (fishbowl)); |
466 | if (frame_clock == NULL) |
467 | return; |
468 | |
469 | start_counter = gdk_frame_clock_get_history_start (frame_clock); |
470 | end_counter = gdk_frame_clock_get_frame_counter (frame_clock); |
471 | start = gdk_frame_clock_get_timings (frame_clock, frame_counter: start_counter); |
472 | for (end = gdk_frame_clock_get_timings (frame_clock, frame_counter: end_counter); |
473 | end_counter > start_counter && end != NULL && !gdk_frame_timings_get_complete (timings: end); |
474 | end = gdk_frame_clock_get_timings (frame_clock, frame_counter: end_counter)) |
475 | end_counter--; |
476 | if (end_counter - start_counter < 4) |
477 | return; |
478 | |
479 | start_timestamp = gdk_frame_timings_get_presentation_time (timings: start); |
480 | end_timestamp = gdk_frame_timings_get_presentation_time (timings: end); |
481 | if (start_timestamp == 0 || end_timestamp == 0) |
482 | { |
483 | start_timestamp = gdk_frame_timings_get_frame_time (timings: start); |
484 | end_timestamp = gdk_frame_timings_get_frame_time (timings: end); |
485 | } |
486 | |
487 | n_frames = end_counter - start_counter; |
488 | priv->framerate = ((double) n_frames) * G_USEC_PER_SEC / (end_timestamp - start_timestamp); |
489 | priv->framerate = ((int)(priv->framerate * 100))/100.0; |
490 | |
491 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_FRAMERATE]); |
492 | |
493 | if (!priv->benchmark) |
494 | return; |
495 | |
496 | interval = gdk_frame_timings_get_refresh_interval (timings: end); |
497 | if (interval == 0) |
498 | { |
499 | interval = guess_refresh_interval (frame_clock); |
500 | if (interval == 0) |
501 | return; |
502 | } |
503 | expected_frames = round (x: (double) (end_timestamp - start_timestamp) / interval); |
504 | |
505 | if (n_frames >= expected_frames) |
506 | { |
507 | if (priv->last_benchmark_change > 0) |
508 | priv->last_benchmark_change *= 2; |
509 | else |
510 | priv->last_benchmark_change = 1; |
511 | } |
512 | else if (n_frames + 1 < expected_frames) |
513 | { |
514 | if (priv->last_benchmark_change < 0) |
515 | priv->last_benchmark_change--; |
516 | else |
517 | priv->last_benchmark_change = -1; |
518 | } |
519 | else |
520 | { |
521 | priv->last_benchmark_change = 0; |
522 | } |
523 | |
524 | gtk_fishbowl_set_count (fishbowl, MAX (1, (int) priv->count + priv->last_benchmark_change)); |
525 | } |
526 | |
527 | static gboolean |
528 | gtk_fishbowl_tick (GtkWidget *widget, |
529 | GdkFrameClock *frame_clock, |
530 | gpointer unused) |
531 | { |
532 | GtkFishbowl *fishbowl = GTK_FISHBOWL (widget); |
533 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
534 | GtkFishbowlChild *child; |
535 | gint64 frame_time, elapsed; |
536 | GHashTableIter iter; |
537 | gpointer key, value; |
538 | gboolean do_update; |
539 | |
540 | frame_time = gdk_frame_clock_get_frame_time (frame_clock: gtk_widget_get_frame_clock (widget)); |
541 | elapsed = frame_time - priv->last_frame_time; |
542 | do_update = frame_time / priv->update_delay != priv->last_frame_time / priv->update_delay; |
543 | priv->last_frame_time = frame_time; |
544 | |
545 | /* last frame was 0, so we're just starting to animate */ |
546 | if (elapsed == frame_time) |
547 | return G_SOURCE_CONTINUE; |
548 | |
549 | g_hash_table_iter_init (iter: &iter, hash_table: priv->children); |
550 | while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value)) |
551 | { |
552 | child = value; |
553 | |
554 | child->x += child->dx * ((double) elapsed / G_USEC_PER_SEC); |
555 | child->y += child->dy * ((double) elapsed / G_USEC_PER_SEC); |
556 | |
557 | if (child->x <= 0) |
558 | { |
559 | child->x = 0; |
560 | child->dx = new_speed (); |
561 | } |
562 | else if (child->x >= 1) |
563 | { |
564 | child->x = 1; |
565 | child->dx = - new_speed (); |
566 | } |
567 | |
568 | if (child->y <= 0) |
569 | { |
570 | child->y = 0; |
571 | child->dy = new_speed (); |
572 | } |
573 | else if (child->y >= 1) |
574 | { |
575 | child->y = 1; |
576 | child->dy = - new_speed (); |
577 | } |
578 | } |
579 | |
580 | gtk_widget_queue_allocate (widget); |
581 | |
582 | if (do_update) |
583 | gtk_fishbowl_do_update (fishbowl); |
584 | |
585 | return G_SOURCE_CONTINUE; |
586 | } |
587 | |
588 | void |
589 | gtk_fishbowl_set_animating (GtkFishbowl *fishbowl, |
590 | gboolean animating) |
591 | { |
592 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
593 | |
594 | if (gtk_fishbowl_get_animating (fishbowl) == animating) |
595 | return; |
596 | |
597 | if (animating) |
598 | { |
599 | priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (fishbowl), |
600 | callback: gtk_fishbowl_tick, |
601 | NULL, |
602 | NULL); |
603 | } |
604 | else |
605 | { |
606 | priv->last_frame_time = 0; |
607 | gtk_widget_remove_tick_callback (GTK_WIDGET (fishbowl), id: priv->tick_id); |
608 | priv->tick_id = 0; |
609 | priv->framerate = 0; |
610 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_FRAMERATE]); |
611 | } |
612 | |
613 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_ANIMATING]); |
614 | } |
615 | |
616 | double |
617 | gtk_fishbowl_get_framerate (GtkFishbowl *fishbowl) |
618 | { |
619 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
620 | |
621 | return priv->framerate; |
622 | } |
623 | |
624 | gint64 |
625 | gtk_fishbowl_get_update_delay (GtkFishbowl *fishbowl) |
626 | { |
627 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
628 | |
629 | return priv->update_delay; |
630 | } |
631 | |
632 | void |
633 | gtk_fishbowl_set_update_delay (GtkFishbowl *fishbowl, |
634 | gint64 update_delay) |
635 | { |
636 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
637 | |
638 | if (priv->update_delay == update_delay) |
639 | return; |
640 | |
641 | priv->update_delay = update_delay; |
642 | |
643 | g_object_notify_by_pspec (G_OBJECT (fishbowl), pspec: props[PROP_UPDATE_DELAY]); |
644 | } |
645 | |
646 | void |
647 | gtk_fishbowl_set_creation_func (GtkFishbowl *fishbowl, |
648 | GtkFishCreationFunc creation_func) |
649 | { |
650 | GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (self: fishbowl); |
651 | |
652 | g_object_freeze_notify (G_OBJECT (fishbowl)); |
653 | |
654 | gtk_fishbowl_set_count (fishbowl, count: 0); |
655 | priv->last_benchmark_change = 0; |
656 | |
657 | priv->creation_func = creation_func; |
658 | |
659 | gtk_fishbowl_set_count (fishbowl, count: 1); |
660 | |
661 | g_object_thaw_notify (G_OBJECT (fishbowl)); |
662 | } |
663 | |