]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclock.c
x11: Remove unused argument
[~andy/gtk] / gdk / gdkframeclock.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2010.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28
29 #include "gdkframeclockprivate.h"
30 #include "gdkinternals.h"
31
32 /**
33  * SECTION:frameclock
34  * @Short_description: Frame clock syncs painting to a window or display
35  * @Title: Frame clock
36  *
37  * A #GdkFrameClock tells the application when to update and repaint a
38  * window. This may be synced to the vertical refresh rate of the
39  * monitor, for example. Even when the frame clock uses a simple timer
40  * rather than a hardware-based vertical sync, the frame clock helps
41  * because it ensures everything paints at the same time (reducing the
42  * total number of frames). The frame clock can also automatically
43  * stop painting when it knows the frames will not be visible, or
44  * scale back animation framerates.
45  *
46  * #GdkFrameClock is designed to be compatible with an OpenGL-based
47  * implementation or with mozRequestAnimationFrame in Firefox,
48  * for example.
49  *
50  * A frame clock is idle until someone requests a frame with
51  * gdk_frame_clock_request_phase(). At some later point that makes
52  * sense for the synchronization being implemented, the clock will
53  * process a frame and emit signals for each phase that has been
54  * requested. (See the signals of the #GdkFrameClock class for
55  * documentation of the phases. GDK_FRAME_CLOCK_PHASE_UPDATE and the
56  * ::update signal are most interesting for application writers, and
57  * are used to update the animations, using the frame time given by
58  * gdk_frame_clock_get_frame_time().
59  *
60  * The frame time is reported in microseconds and generally in the same
61  * timescale as g_get_monotonic_time(), however, it is not the same
62  * as g_get_monotonic_time(). The frame time does not advance during
63  * the time a frame is being painted, and outside of a frame, an attempt
64  * is made so that all calls to gdk_frame_clock_get_frame_time() that
65  * are called at a "similar" time get the same value. This means that
66  * if different animations are timed by looking at the difference in
67  * time between an initial value from gdk_frame_clock_get_frame_time()
68  * and the value inside the ::update signal of the clock, they will
69  * stay exactly synchronized.
70  */
71
72 G_DEFINE_ABSTRACT_TYPE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
73
74 enum {
75   FLUSH_EVENTS,
76   BEFORE_PAINT,
77   UPDATE,
78   LAYOUT,
79   PAINT,
80   AFTER_PAINT,
81   RESUME_EVENTS,
82   LAST_SIGNAL
83 };
84
85 static guint signals[LAST_SIGNAL];
86
87 #define FRAME_HISTORY_MAX_LENGTH 16
88
89 struct _GdkFrameClockPrivate
90 {
91   gint64 frame_counter;
92   gint n_timings;
93   gint current;
94   GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
95 };
96
97 static void
98 gdk_frame_clock_finalize (GObject *object)
99 {
100   GdkFrameClockPrivate *priv = GDK_FRAME_CLOCK (object)->priv;
101   int i;
102
103   for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
104     if (priv->timings[i] != 0)
105       gdk_frame_timings_unref (priv->timings[i]);
106
107   G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
108 }
109
110 static void
111 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
112 {
113   GObjectClass *gobject_class = (GObjectClass*) klass;
114
115   gobject_class->finalize     = gdk_frame_clock_finalize;
116
117   /**
118    * GdkFrameClock::flush-events:
119    * @clock: the frame clock emitting the signal
120    *
121    * This signal is used to flush pending motion events that
122    * are being batched up and compressed together. Applications
123    * should not handle this signal.
124    */
125   signals[FLUSH_EVENTS] =
126     g_signal_new (g_intern_static_string ("flush-events"),
127                   GDK_TYPE_FRAME_CLOCK,
128                   G_SIGNAL_RUN_LAST,
129                   0,
130                   NULL, NULL,
131                   g_cclosure_marshal_VOID__VOID,
132                   G_TYPE_NONE, 0);
133
134   /**
135    * GdkFrameClock::before-paint:
136    * @clock: the frame clock emitting the signal
137    *
138    * This signal begins processing of the frame. Applications
139    * should generally not handle this signal.
140    */
141   signals[BEFORE_PAINT] =
142     g_signal_new (g_intern_static_string ("before-paint"),
143                   GDK_TYPE_FRAME_CLOCK,
144                   G_SIGNAL_RUN_LAST,
145                   0,
146                   NULL, NULL,
147                   g_cclosure_marshal_VOID__VOID,
148                   G_TYPE_NONE, 0);
149
150   /**
151    * GdkFrameClock::update:
152    * @clock: the frame clock emitting the signal
153    *
154    * This signal is emitted as the first step of toolkit and
155    * application processing of the frame. Animations should
156    * be updated using gdk_frame_clock_get_frame_time().
157    * Applications can connect directly to this signal, or
158    * use gtk_widget_add_tick_callback() as a more convenient
159    * interface.
160    */
161   signals[UPDATE] =
162     g_signal_new (g_intern_static_string ("update"),
163                   GDK_TYPE_FRAME_CLOCK,
164                   G_SIGNAL_RUN_LAST,
165                   0,
166                   NULL, NULL,
167                   g_cclosure_marshal_VOID__VOID,
168                   G_TYPE_NONE, 0);
169
170   /**
171    * GdkFrameClock::layout:
172    * @clock: the frame clock emitting the signal
173    *
174    * This signal is emitted as the second step of toolkit and
175    * application processing of the frame. Any work to update
176    * sizes and positions of application elements should be
177    * performed. GTK normally handles this internally.
178    */
179   signals[LAYOUT] =
180     g_signal_new (g_intern_static_string ("layout"),
181                   GDK_TYPE_FRAME_CLOCK,
182                   G_SIGNAL_RUN_LAST,
183                   0,
184                   NULL, NULL,
185                   g_cclosure_marshal_VOID__VOID,
186                   G_TYPE_NONE, 0);
187
188   /**
189    * GdkFrameClock::paint:
190    * @clock: the frame clock emitting the signal
191    *
192    * This signal is emitted as the third step of toolkit and
193    * application processing of the frame. The frame is
194    * repainted. GDK normally handles this internally and
195    * produce expose events, which are turned into GTK
196    * GtkWidget::draw signals.
197    */
198   signals[PAINT] =
199     g_signal_new (g_intern_static_string ("paint"),
200                   GDK_TYPE_FRAME_CLOCK,
201                   G_SIGNAL_RUN_LAST,
202                   0,
203                   NULL, NULL,
204                   g_cclosure_marshal_VOID__VOID,
205                   G_TYPE_NONE, 0);
206
207   /**
208    * GdkFrameClock::after-paint:
209    * @clock: the frame clock emitting the signal
210    *
211    * This signal ends processing of the frame. Applications
212    * should generally not handle this signal.
213    */
214   signals[AFTER_PAINT] =
215     g_signal_new (g_intern_static_string ("after-paint"),
216                   GDK_TYPE_FRAME_CLOCK,
217                   G_SIGNAL_RUN_LAST,
218                   0,
219                   NULL, NULL,
220                   g_cclosure_marshal_VOID__VOID,
221                   G_TYPE_NONE, 0);
222
223   /**
224    * GdkFrameClock::resume-events:
225    * @clock: the frame clock emitting the signal
226    *
227    * This signal is emitted after processing of the frame is
228    * finished, and is handled internally by GTK+ to resume normal
229    * event processing. Applications should not handle this signal.
230    */
231   signals[RESUME_EVENTS] =
232     g_signal_new (g_intern_static_string ("resume-events"),
233                   GDK_TYPE_FRAME_CLOCK,
234                   G_SIGNAL_RUN_LAST,
235                   0,
236                   NULL, NULL,
237                   g_cclosure_marshal_VOID__VOID,
238                   G_TYPE_NONE, 0);
239
240   g_type_class_add_private (klass, sizeof (GdkFrameClockPrivate));
241 }
242
243 static void
244 gdk_frame_clock_init (GdkFrameClock *clock)
245 {
246   GdkFrameClockPrivate *priv;
247
248   clock->priv = G_TYPE_INSTANCE_GET_PRIVATE (clock,
249                                              GDK_TYPE_FRAME_CLOCK,
250                                              GdkFrameClockPrivate);
251   priv = clock->priv;
252
253   priv->frame_counter = -1;
254   priv->current = FRAME_HISTORY_MAX_LENGTH - 1;
255 }
256
257 /**
258  * gdk_frame_clock_get_frame_time:
259  * @frame_clock: a #GdkFrameClock
260  *
261  * Gets the time that should currently be used for animations.  Inside
262  * the processing of a frame, it's the time used to compute the
263  * animation position of everything in a frame. Outside of a frame, it's
264  * the time of the conceptual "previous frame," which may be either
265  * the actual previous frame time, or if that's too old, an updated
266  * time.
267  *
268  * Since: 3.8
269  * Return value: a timestamp in microseconds, in the timescale of
270  *  of g_get_monotonic_time().
271  */
272 gint64
273 gdk_frame_clock_get_frame_time (GdkFrameClock *frame_clock)
274 {
275   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
276
277   return GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->get_frame_time (frame_clock);
278 }
279
280 /**
281  * gdk_frame_clock_request_phase:
282  * @frame_clock: a #GdkFrameClock
283  *
284  * Asks the frame clock to run a particular phase. The signal
285  * corresponding the requested phase will be emitted the next
286  * time the frame clock processes. Multiple calls to
287  * gdk_frame_clock_request_phase() will be combined togethe
288  * and only one frame processed. If you are displaying animated
289  * content and want to continually request the
290  * %GDK_FRAME_CLOCK_PHASE_UPDATE phase for a period of time,
291  * you should use gdk_frame_clock_begin_updating() instead, since
292  * this allows GTK+ to adjust system parameters to get maximally
293  * smooth animations.
294  *
295  * Since: 3.8
296  */
297 void
298 gdk_frame_clock_request_phase (GdkFrameClock      *frame_clock,
299                                GdkFrameClockPhase  phase)
300 {
301   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
302
303   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->request_phase (frame_clock, phase);
304 }
305
306 /**
307  * gdk_frame_clock_begin_updating:
308  * @frame_clock: a #GdkFrameClock
309  *
310  * Starts updates for an animation. Until a matching call to
311  * gdk_frame_clock_end_updating() is made, the frame clock will continually
312  * request a new frame with the %GDK_FRAME_CLOCK_PHASE_UPDATE phase.
313  * This function may be called multiple times and frames will be
314  * requested until gdk_frame_clock_end_updating() is called the same
315  * number of times.
316  *
317  * Since: 3.8
318  */
319 void
320 gdk_frame_clock_begin_updating (GdkFrameClock *frame_clock)
321 {
322   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
323
324   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->begin_updating (frame_clock);
325 }
326
327 /**
328  * gdk_frame_clock_end_updating:
329  * @frame_clock: a #GdkFrameClock
330  *
331  * Stops updates for an animation. See the documentation for
332  * gdk_frame_clock_begin_updating().
333  *
334  * Since: 3.8
335  */
336 void
337 gdk_frame_clock_end_updating (GdkFrameClock *frame_clock)
338 {
339   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
340
341   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->end_updating (frame_clock);
342 }
343
344 void
345 _gdk_frame_clock_freeze (GdkFrameClock *clock)
346 {
347   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
348
349   GDK_FRAME_CLOCK_GET_CLASS (clock)->freeze (clock);
350 }
351
352
353 void
354 _gdk_frame_clock_thaw (GdkFrameClock *clock)
355 {
356   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
357
358   GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
359 }
360
361 /**
362  * gdk_frame_clock_get_frame_counter:
363  * @frame_clock: a #GdkFrameClock
364  *
365  * A #GdkFrameClock maintains a 64-bit counter that increments for
366  * each frame drawn.
367  *
368  * Returns: inside frame processing, the value of the frame counter
369  *  for the current frame. Outside of frame processing, the frame
370  *   counter for the last frame.
371  * Since: 3.8
372  */
373 gint64
374 gdk_frame_clock_get_frame_counter (GdkFrameClock *frame_clock)
375 {
376   GdkFrameClockPrivate *priv;
377
378   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
379
380   priv = frame_clock->priv;
381
382   return priv->frame_counter;
383 }
384
385 /**
386  * gdk_frame_clock_get_history_start:
387  * @frame_clock: a #GdkFrameClock
388  *
389  * #GdkFrameClock internally keeps a history of #GdkFrameTiming
390  * objects for recent frames that can be retrieved with
391  * gdk_frame_clock_get_timings(). The set of stored frames
392  * is the set from the counter values given by
393  * gdk_frame_clock_get_history_start() and
394  * gdk_frame_clock_get_frame_counter(), inclusive.
395  *
396  * Return value: the frame counter value for the oldest frame
397  *  that is available in the internal frame history of the
398  *  #GdkFrameClock.
399  * Since: 3.8
400  */
401 gint64
402 gdk_frame_clock_get_history_start (GdkFrameClock *frame_clock)
403 {
404   GdkFrameClockPrivate *priv;
405
406   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
407
408   priv = frame_clock->priv;
409
410   return priv->frame_counter + 1 - priv->n_timings;
411 }
412
413 void
414 _gdk_frame_clock_begin_frame (GdkFrameClock *frame_clock)
415 {
416   GdkFrameClockPrivate *priv;
417
418   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
419
420   priv = frame_clock->priv;
421
422   priv->frame_counter++;
423   priv->current = (priv->current + 1) % FRAME_HISTORY_MAX_LENGTH;
424
425   if (priv->n_timings < FRAME_HISTORY_MAX_LENGTH)
426     priv->n_timings++;
427   else
428     {
429       gdk_frame_timings_unref(priv->timings[priv->current]);
430     }
431
432   priv->timings[priv->current] = _gdk_frame_timings_new (priv->frame_counter);
433 }
434
435 /**
436  * gdk_frame_clock_get_timings:
437  * @frame_clock: a #GdkFrameClock
438  * @frame_counter: the frame counter value identifying the frame to
439  *  be received.
440  *
441  * Retrieves a #GdkFrameTimings object holding timing information
442  * for the current frame or a recent frame. The #GdkFrameTimings
443  * object may not yet be complete: see gdk_frame_timings_get_complete().
444  *
445  * Return value: the #GdkFrameTimings object for the specified
446  *  frame, or %NULL if it is not available. See
447  *  gdk_frame_clock_get_history_start().
448  * Since: 3.8
449  */
450 GdkFrameTimings *
451 gdk_frame_clock_get_timings (GdkFrameClock *frame_clock,
452                              gint64         frame_counter)
453 {
454   GdkFrameClockPrivate *priv;
455   gint pos;
456
457   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), NULL);
458
459   priv = frame_clock->priv;
460
461   if (frame_counter > priv->frame_counter)
462     return NULL;
463
464   if (frame_counter <= priv->frame_counter - priv->n_timings)
465     return NULL;
466
467   pos = (priv->current - (priv->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
468
469   return priv->timings[pos];
470 }
471
472 /**
473  * gdk_frame_clock_get_current_timings:
474  * @frame_clock: a #GdkFrameClock
475  *
476  * Gets the frame timings for the current frame.
477  *
478  * Returns: the #GdkFrameTimings for the frame currently being
479  *  processed, or even no frame is being processed, for the
480  *  previous frame. Before any frames have been procesed,
481  *  returns %NULL.
482  * Since: 3.8
483  */
484 GdkFrameTimings *
485 gdk_frame_clock_get_current_timings (GdkFrameClock *frame_clock)
486 {
487   GdkFrameClockPrivate *priv;
488
489   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (frame_clock), 0);
490
491   priv = frame_clock->priv;
492
493   return gdk_frame_clock_get_timings (frame_clock, priv->frame_counter);
494 }
495
496
497 #ifdef G_ENABLE_DEBUG
498 void
499 _gdk_frame_clock_debug_print_timings (GdkFrameClock   *clock,
500                                       GdkFrameTimings *timings)
501 {
502   gint64 previous_frame_time = 0;
503   GdkFrameTimings *previous_timings = gdk_frame_clock_get_timings (clock,
504                                                                    timings->frame_counter - 1);
505
506   if (previous_timings != NULL)
507     previous_frame_time = previous_timings->frame_time;
508
509   g_print ("%5" G_GINT64_FORMAT ":", timings->frame_counter);
510   if (previous_frame_time != 0)
511     {
512       g_print (" interval=%-4.1f", (timings->frame_time - previous_frame_time) / 1000.);
513       g_print (timings->slept_before ?  " (sleep)" : "        ");
514     }
515   if (timings->layout_start_time != 0)
516     g_print (" layout_start=%-4.1f", (timings->layout_start_time - timings->frame_time) / 1000.);
517   if (timings->paint_start_time != 0)
518     g_print (" paint_start=%-4.1f", (timings->paint_start_time - timings->frame_time) / 1000.);
519   if (timings->frame_end_time != 0)
520     g_print (" frame_end=%-4.1f", (timings->frame_end_time - timings->frame_time) / 1000.);
521   if (timings->presentation_time != 0)
522     g_print (" present=%-4.1f", (timings->presentation_time - timings->frame_time) / 1000.);
523   if (timings->predicted_presentation_time != 0)
524     g_print (" predicted=%-4.1f", (timings->predicted_presentation_time - timings->frame_time) / 1000.);
525   if (timings->refresh_interval != 0)
526     g_print (" refresh_interval=%-4.1f", timings->refresh_interval / 1000.);
527   g_print ("\n");
528 }
529 #endif /* G_ENABLE_DEBUG */
530
531 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
532 #define MAX_HISTORY_AGE 150000         /* 150ms */
533
534 /**
535  * gdk_frame_clock_get_refresh_info:
536  * @frame_clock: a #GdkFrameClock
537  * @base_time: base time for determining a presentaton time
538  * @refresh_interval_return: a location to store the determined refresh
539  *  interval, or %NULL. A default refresh interval of 1/60th of
540  *  a second will be stored if no history is present.
541  * @presentation_time_return: a location to store the next
542  *  candidate presentation time after the given base time.
543  *  0 will be will be stored if no history is present.
544  *
545  * Using the frame history stored in the frame clock, finds the last
546  * known presentation time and refresh interval, and assuming that
547  * presentation times are separated by the refresh interval,
548  * predicts a presentation time that is a multiple of the refresh
549  * interval after the last presentation time, and later than @base_time.
550  *
551  * Since: 3.8
552  */
553 void
554 gdk_frame_clock_get_refresh_info (GdkFrameClock *frame_clock,
555                                   gint64         base_time,
556                                   gint64        *refresh_interval_return,
557                                   gint64        *presentation_time_return)
558 {
559   gint64 frame_counter;
560
561   g_return_if_fail (GDK_IS_FRAME_CLOCK (frame_clock));
562
563   frame_counter = gdk_frame_clock_get_frame_counter (frame_clock);
564
565   if (presentation_time_return)
566     *presentation_time_return = 0;
567   if (refresh_interval_return)
568     *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
569
570   while (TRUE)
571     {
572       GdkFrameTimings *timings = gdk_frame_clock_get_timings (frame_clock, frame_counter);
573       gint64 presentation_time;
574       gint64 refresh_interval;
575
576       if (timings == NULL)
577         return;
578
579       refresh_interval = timings->refresh_interval;
580       presentation_time = timings->presentation_time;
581
582       if (presentation_time != 0)
583         {
584           if (presentation_time > base_time - MAX_HISTORY_AGE &&
585               presentation_time_return)
586             {
587               if (refresh_interval == 0)
588                 refresh_interval = DEFAULT_REFRESH_INTERVAL;
589
590               if (refresh_interval_return)
591                 *refresh_interval_return = refresh_interval;
592
593               while (presentation_time < base_time)
594                 presentation_time += refresh_interval;
595
596               if (presentation_time_return)
597                 *presentation_time_return = presentation_time;
598             }
599
600           return;
601         }
602
603       frame_counter--;
604     }
605 }