]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclock.c
08789ebb048d871148aa3714be4cd25722de458e
[~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 "gdkframeclock.h"
30
31 /**
32  * SECTION:frameclock
33  * @Short_description: Frame clock syncs painting to a window or display
34  * @Title: Frame clock
35  *
36  * A #GdkFrameClock tells the application when to repaint a window.
37  * This may be synced to the vertical refresh rate of the monitor, for
38  * example. Even when the frame clock uses a simple timer rather than
39  * a hardware-based vertical sync, the frame clock helps because it
40  * ensures everything paints at the same time (reducing the total
41  * number of frames). The frame clock can also automatically stop
42  * painting when it knows the frames will not be visible, or scale back
43  * animation framerates.
44  *
45  * #GdkFrameClock is designed to be compatible with an OpenGL-based
46  * implementation or with mozRequestAnimationFrame in Firefox,
47  * for example.
48  *
49  * A frame clock is idle until someone requests a frame with
50  * gdk_frame_clock_request_phase(). At that time, the frame clock
51  * emits its GdkFrameClock:frame-requested signal if no frame was
52  * already pending.
53  *
54  * At some later time after the frame is requested, the frame clock
55  * MAY indicate that a frame should be painted. To paint a frame the
56  * clock will: Emit GdkFrameClock:before-paint; update the frame time
57  * in the default handler for GdkFrameClock:before-paint; emit
58  * GdkFrameClock:paint; emit GdkFrameClock:after-paint.  The app
59  * should paint in a handler for the paint signal.
60  *
61  * If a given frame is not painted (the clock is idle), the frame time
62  * should still update to a conceptual "last frame." i.e. the frame
63  * time will keep moving forward roughly with wall clock time.
64  *
65  * The frame time is in milliseconds. However, it should not be
66  * thought of as having any particular relationship to wall clock
67  * time. Unlike wall clock time, it "snaps" to conceptual frame times
68  * so is low-resolution; it is guaranteed to never move backward (so
69  * say you reset your computer clock, the frame clock will not reset);
70  * and the frame clock is allowed to drift. For example nicer
71  * results when painting with vertical refresh sync may be obtained by
72  * painting as rapidly as possible, but always incrementing the frame
73  * time by the frame length on each frame. This results in a frame
74  * time that doesn't have a lot to do with wall clock time.
75  */
76
77 G_DEFINE_INTERFACE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
78
79 enum {
80   FRAME_REQUESTED,
81   FLUSH_EVENTS,
82   BEFORE_PAINT,
83   UPDATE,
84   LAYOUT,
85   PAINT,
86   AFTER_PAINT,
87   RESUME_EVENTS,
88   LAST_SIGNAL
89 };
90
91 static guint signals[LAST_SIGNAL];
92
93 static void
94 gdk_frame_clock_default_init (GdkFrameClockInterface *iface)
95 {
96   /**
97    * GdkFrameClock::frame-requested:
98    * @clock: the frame clock emitting the signal
99    *
100    * This signal is emitted when a frame is not pending, and
101    * gdk_frame_clock_request_frame() is called to request a frame.
102    */
103   signals[FRAME_REQUESTED] =
104     g_signal_new (g_intern_static_string ("frame-requested"),
105                   GDK_TYPE_FRAME_CLOCK,
106                   G_SIGNAL_RUN_LAST,
107                   0,
108                   NULL, NULL,
109                   g_cclosure_marshal_VOID__VOID,
110                   G_TYPE_NONE, 0);
111
112   /**
113    * GdkFrameClock::flush-events:
114    * @clock: the frame clock emitting the signal
115    *
116    * FIXME.
117    */
118   signals[FLUSH_EVENTS] =
119     g_signal_new (g_intern_static_string ("flush-events"),
120                   GDK_TYPE_FRAME_CLOCK,
121                   G_SIGNAL_RUN_LAST,
122                   0,
123                   NULL, NULL,
124                   g_cclosure_marshal_VOID__VOID,
125                   G_TYPE_NONE, 0);
126
127   /**
128    * GdkFrameClock::before-paint:
129    * @clock: the frame clock emitting the signal
130    *
131    * This signal is emitted immediately before the paint signal and
132    * indicates that the frame time has been updated, and signal
133    * handlers should perform any preparatory work before painting.
134    */
135   signals[BEFORE_PAINT] =
136     g_signal_new (g_intern_static_string ("before-paint"),
137                   GDK_TYPE_FRAME_CLOCK,
138                   G_SIGNAL_RUN_LAST,
139                   0,
140                   NULL, NULL,
141                   g_cclosure_marshal_VOID__VOID,
142                   G_TYPE_NONE, 0);
143
144   /**
145    * GdkFrameClock::update:
146    * @clock: the frame clock emitting the signal
147    *
148    * FIXME.
149    */
150   signals[UPDATE] =
151     g_signal_new (g_intern_static_string ("update"),
152                   GDK_TYPE_FRAME_CLOCK,
153                   G_SIGNAL_RUN_LAST,
154                   0,
155                   NULL, NULL,
156                   g_cclosure_marshal_VOID__VOID,
157                   G_TYPE_NONE, 0);
158
159   /**
160    * GdkFrameClock::layout:
161    * @clock: the frame clock emitting the signal
162    *
163    * This signal is emitted immediately before the paint signal and
164    * indicates that the frame time has been updated, and signal
165    * handlers should perform any preparatory work before painting.
166    */
167   signals[LAYOUT] =
168     g_signal_new (g_intern_static_string ("layout"),
169                   GDK_TYPE_FRAME_CLOCK,
170                   G_SIGNAL_RUN_LAST,
171                   0,
172                   NULL, NULL,
173                   g_cclosure_marshal_VOID__VOID,
174                   G_TYPE_NONE, 0);
175
176   /**
177    * GdkFrameClock::paint:
178    * @clock: the frame clock emitting the signal
179    *
180    * Signal handlers for this signal should paint the window, screen,
181    * or whatever they normally paint.
182    */
183   signals[PAINT] =
184     g_signal_new (g_intern_static_string ("paint"),
185                   GDK_TYPE_FRAME_CLOCK,
186                   G_SIGNAL_RUN_LAST,
187                   0,
188                   NULL, NULL,
189                   g_cclosure_marshal_VOID__VOID,
190                   G_TYPE_NONE, 0);
191
192   /**
193    * GdkFrameClock::after-paint:
194    * @clock: the frame clock emitting the signal
195    *
196    * This signal is emitted immediately after the paint signal and
197    * allows signal handlers to do anything they'd like to do after
198    * painting has been completed. This is a relatively good time to do
199    * "expensive" processing in order to get it done in between frames.
200    */
201   signals[AFTER_PAINT] =
202     g_signal_new (g_intern_static_string ("after-paint"),
203                   GDK_TYPE_FRAME_CLOCK,
204                   G_SIGNAL_RUN_LAST,
205                   0,
206                   NULL, NULL,
207                   g_cclosure_marshal_VOID__VOID,
208                   G_TYPE_NONE, 0);
209
210   /**
211    * GdkFrameClock::resume-events:
212    * @clock: the frame clock emitting the signal
213    *
214    * FIXME.
215    */
216   signals[RESUME_EVENTS] =
217     g_signal_new (g_intern_static_string ("resume-events"),
218                   GDK_TYPE_FRAME_CLOCK,
219                   G_SIGNAL_RUN_LAST,
220                   0,
221                   NULL, NULL,
222                   g_cclosure_marshal_VOID__VOID,
223                   G_TYPE_NONE, 0);
224 }
225
226 /**
227  * gdk_frame_clock_get_frame_time:
228  * @clock: the clock
229  *
230  * Gets the time that should currently be used for animations.  Inside
231  * a paint, it's the time used to compute the animation position of
232  * everything in a frame. Outside a paint, it's the time of the
233  * conceptual "previous frame," which may be either the actual
234  * previous frame time, or if that's too old, an updated time.
235  *
236  * The returned time has no relationship to wall clock time.  It
237  * increases roughly at 1 millisecond per wall clock millisecond, and
238  * it never decreases, but its value is only meaningful relative to
239  * previous frame clock times.
240  *
241  *
242  * Since: 3.0
243  * Return value: a timestamp in milliseconds
244  */
245 guint64
246 gdk_frame_clock_get_frame_time (GdkFrameClock *clock)
247 {
248   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
249
250   return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_frame_time (clock);
251 }
252
253 /**
254  * gdk_frame_clock_request_phase:
255  * @clock: the clock
256  *
257  * Asks the frame clock to paint a frame. The frame
258  * may or may not ever be painted (the frame clock may
259  * stop itself for whatever reason), but the goal in
260  * normal circumstances would be to paint the frame
261  * at the next expected frame time. For example
262  * if the clock is running at 60fps the frame would
263  * ideally be painted within 1000/60=16 milliseconds.
264  *
265  * Since: 3.0
266  */
267 void
268 gdk_frame_clock_request_phase (GdkFrameClock      *clock,
269                                GdkFrameClockPhase  phase)
270 {
271   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
272
273   GDK_FRAME_CLOCK_GET_IFACE (clock)->request_phase (clock, phase);
274 }
275
276
277 void
278 gdk_frame_clock_freeze (GdkFrameClock *clock)
279 {
280   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
281
282   GDK_FRAME_CLOCK_GET_IFACE (clock)->freeze (clock);
283 }
284
285
286 void
287 gdk_frame_clock_thaw (GdkFrameClock *clock)
288 {
289   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
290
291   GDK_FRAME_CLOCK_GET_IFACE (clock)->thaw (clock);
292 }
293
294 /**
295  * gdk_frame_clock_get_history:
296  * @clock: the clock
297  *
298  * Gets the #GdkFrameHistory for the frame clock.
299  *
300  * Since: 3.8
301  * Return value: (transfer none): the frame history object
302  */
303 GdkFrameHistory *
304 gdk_frame_clock_get_history (GdkFrameClock *clock)
305 {
306   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
307
308   return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_history (clock);
309 }
310
311 /**
312  * gdk_frame_clock_get_requested:
313  * @clock: the clock
314  *
315  * Gets whether a frame paint has been requested but has not been
316  * performed.
317  *
318  *
319  * Since: 3.0
320  * Return value: TRUE if a frame paint is pending
321  */
322 GdkFrameClockPhase
323 gdk_frame_clock_get_requested (GdkFrameClock *clock)
324 {
325   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), FALSE);
326
327   return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_requested (clock);
328 }
329
330 /**
331  * gdk_frame_clock_get_frame_time_val:
332  * @clock: the clock
333  * @timeval: #GTimeVal to fill in with frame time
334  *
335  * Like gdk_frame_clock_get_frame_time() but returns the time as a
336  * #GTimeVal which may be handy with some APIs (such as
337  * #GdkPixbufAnimation).
338  */
339 void
340 gdk_frame_clock_get_frame_time_val (GdkFrameClock *clock,
341                                     GTimeVal      *timeval)
342 {
343   guint64 time_ms;
344
345   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
346
347   time_ms = gdk_frame_clock_get_frame_time (clock);
348
349   timeval->tv_sec = time_ms / 1000;
350   timeval->tv_usec = (time_ms % 1000) * 1000;
351 }
352
353 /**
354  * gdk_frame_clock_frame_requested:
355  * @clock: the clock
356  *
357  * Emits the frame-requested signal. Used in implementations of the
358  * #GdkFrameClock interface.
359  */
360 void
361 gdk_frame_clock_frame_requested (GdkFrameClock *clock)
362 {
363   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
364
365   g_signal_emit (G_OBJECT (clock),
366                  signals[FRAME_REQUESTED], 0);
367 }
368
369 GdkFrameTimings *
370 gdk_frame_clock_get_current_frame_timings (GdkFrameClock *clock)
371 {
372   GdkFrameHistory *history;
373   gint64 frame_counter;
374
375   g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
376
377   history = gdk_frame_clock_get_history (clock);
378   frame_counter = gdk_frame_history_get_frame_counter (history);
379   return gdk_frame_history_get_timings (history, frame_counter);
380 }
381
382
383 #define DEFAULT_REFRESH_INTERVAL 16667 /* 16.7ms (1/60th second) */
384 #define MAX_HISTORY_AGE 150000         /* 150ms */
385
386 void
387 gdk_frame_clock_get_refresh_info (GdkFrameClock *clock,
388                                   gint64         base_time,
389                                   gint64        *refresh_interval_return,
390                                   gint64        *presentation_time_return)
391 {
392   GdkFrameHistory *history;
393   gint64 frame_counter;
394
395   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
396
397   history = gdk_frame_clock_get_history (clock);
398   frame_counter = gdk_frame_history_get_frame_counter (history);
399
400   if (presentation_time_return)
401     *presentation_time_return = 0;
402   if (refresh_interval_return)
403     *refresh_interval_return = DEFAULT_REFRESH_INTERVAL;
404
405   while (TRUE)
406     {
407       GdkFrameTimings *timings = gdk_frame_history_get_timings (history, frame_counter);
408       gint64 presentation_time;
409       gint64 refresh_interval;
410
411       if (timings == NULL)
412         return;
413
414       refresh_interval = gdk_frame_timings_get_refresh_interval (timings);
415       presentation_time = gdk_frame_timings_get_presentation_time (timings);
416
417       if (presentation_time != 0)
418         {
419           if (presentation_time > base_time - MAX_HISTORY_AGE &&
420               presentation_time_return)
421             {
422               if (refresh_interval == 0)
423                 refresh_interval = DEFAULT_REFRESH_INTERVAL;
424
425               if (refresh_interval_return)
426                 *refresh_interval_return = refresh_interval;
427
428               while (presentation_time < base_time)
429                 presentation_time += refresh_interval;
430
431               if (presentation_time_return)
432                 *presentation_time_return = presentation_time;
433             }
434
435           return;
436         }
437
438       frame_counter--;
439     }
440 }