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