]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclockidle.c
gdk_frame_clock_get_frame_time(): use gint64 for time
[~andy/gtk] / gdk / gdkframeclockidle.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 "gdkinternals.h"
30 #include "gdkframeclockprivate.h"
31 #include "gdkframeclockidle.h"
32 #include "gdk.h"
33
34 #define FRAME_INTERVAL 16667 // microseconds
35
36 struct _GdkFrameClockIdlePrivate
37 {
38   GTimer *timer;
39   /* timer_base is used to avoid ever going backward */
40   gint64 timer_base;
41   gint64 frame_time;
42   gint64 min_next_frame_time;
43   gint64 sleep_serial;
44
45   guint flush_idle_id;
46   guint paint_idle_id;
47   guint freeze_count;
48
49   GdkFrameClockPhase requested;
50   GdkFrameClockPhase phase;
51
52   guint in_paint_idle : 1;
53 };
54
55 static gboolean gdk_frame_clock_flush_idle (void *data);
56 static gboolean gdk_frame_clock_paint_idle (void *data);
57
58 static void gdk_frame_clock_idle_finalize             (GObject                *object);
59
60 G_DEFINE_TYPE (GdkFrameClockIdle, gdk_frame_clock_idle, GDK_TYPE_FRAME_CLOCK)
61
62 static gint64 sleep_serial;
63 static gint64 sleep_source_prepare_time;
64 static GSource *sleep_source;
65
66 gboolean
67 sleep_source_prepare (GSource *source,
68                       gint    *timeout)
69 {
70   sleep_source_prepare_time = g_source_get_time (source);
71   *timeout = -1;
72   return FALSE;
73 }
74
75 gboolean
76 sleep_source_check (GSource *source)
77 {
78   if (g_source_get_time (source) != sleep_source_prepare_time)
79     sleep_serial++;
80
81   return FALSE;
82 }
83
84 gboolean
85 sleep_source_dispatch (GSource     *source,
86                        GSourceFunc  callback,
87                        gpointer     user_data)
88 {
89   return TRUE;
90 }
91
92 static GSourceFuncs sleep_source_funcs = {
93   sleep_source_prepare,
94   sleep_source_check,
95   sleep_source_dispatch,
96   NULL /* finalize */
97 };
98
99 static gint64
100 get_sleep_serial (void)
101 {
102   if (sleep_source == NULL)
103     {
104       sleep_source = g_source_new (&sleep_source_funcs, sizeof (GSource));
105
106       g_source_set_priority (sleep_source, G_PRIORITY_HIGH);
107       g_source_attach (sleep_source, NULL);
108       g_source_unref (sleep_source);
109     }
110
111   return sleep_serial;
112 }
113
114 static void
115 gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
116 {
117   GdkFrameClockIdlePrivate *priv;
118
119   frame_clock_idle->priv = G_TYPE_INSTANCE_GET_PRIVATE (frame_clock_idle,
120                                                         GDK_TYPE_FRAME_CLOCK_IDLE,
121                                                         GdkFrameClockIdlePrivate);
122   priv = frame_clock_idle->priv;
123
124   priv->timer = g_timer_new ();
125   priv->freeze_count = 0;
126 }
127
128 static void
129 gdk_frame_clock_idle_finalize (GObject *object)
130 {
131   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (object)->priv;
132
133   g_timer_destroy (priv->timer);
134
135   G_OBJECT_CLASS (gdk_frame_clock_idle_parent_class)->finalize (object);
136 }
137
138 static gint64
139 compute_frame_time (GdkFrameClockIdle *idle)
140 {
141   GdkFrameClockIdlePrivate *priv = idle->priv;
142   gint64 computed_frame_time;
143   gint64 elapsed;
144
145   elapsed = g_get_monotonic_time () + priv->timer_base;
146   if (elapsed < priv->frame_time)
147     {
148       /* clock went backward. adapt to that by forevermore increasing
149        * timer_base.  For now, assume we've gone forward in time 1ms.
150        */
151       /* hmm. just fix GTimer? */
152       computed_frame_time = priv->frame_time + 1;
153       priv->timer_base += (priv->frame_time - elapsed) + 1;
154     }
155   else
156     {
157       computed_frame_time = elapsed;
158     }
159
160   return computed_frame_time;
161 }
162
163 static gint64
164 gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
165 {
166   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
167   gint64 computed_frame_time;
168
169   /* can't change frame time during a paint */
170   if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE &&
171       priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS)
172     return priv->frame_time;
173
174   /* Outside a paint, pick something close to "now" */
175   computed_frame_time = compute_frame_time (GDK_FRAME_CLOCK_IDLE (clock));
176
177   /* 16ms is 60fps. We only update frame time that often because we'd
178    * like to try to keep animations on the same start times.
179    * get_frame_time() would normally be used outside of a paint to
180    * record an animation start time for example.
181    */
182   if ((computed_frame_time - priv->frame_time) > FRAME_INTERVAL)
183     priv->frame_time = computed_frame_time;
184
185   return priv->frame_time;
186 }
187
188 static void
189 maybe_start_idle (GdkFrameClockIdle *clock_idle)
190 {
191   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
192
193   if (priv->freeze_count == 0 && priv->requested != 0)
194     {
195       guint min_interval = 0;
196
197       if (priv->min_next_frame_time != 0)
198         {
199           gint64 now = compute_frame_time (clock_idle);
200           gint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now;
201           min_interval = (min_interval_us + 500) / 1000;
202         }
203
204       if (priv->flush_idle_id == 0 &&
205           (priv->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
206         {
207           priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1,
208                                                               min_interval,
209                                                               gdk_frame_clock_flush_idle,
210                                                               g_object_ref (clock_idle),
211                                                               (GDestroyNotify) g_object_unref);
212         }
213
214       if (priv->paint_idle_id == 0 &&
215           !priv->in_paint_idle &&
216           (priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
217         {
218           priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
219                                                               min_interval,
220                                                               gdk_frame_clock_paint_idle,
221                                                               g_object_ref (clock_idle),
222                                                               (GDestroyNotify) g_object_unref);
223         }
224     }
225 }
226
227 static gint64
228 compute_min_next_frame_time (GdkFrameClockIdle *clock_idle,
229                              gint64             last_frame_time)
230 {
231   gint64 presentation_time;
232   gint64 refresh_interval;
233
234   gdk_frame_clock_get_refresh_info (GDK_FRAME_CLOCK (clock_idle),
235                                     last_frame_time,
236                                     &refresh_interval, &presentation_time);
237
238   if (presentation_time == 0)
239     return last_frame_time + refresh_interval;
240   else
241     return presentation_time + refresh_interval / 2;
242 }
243
244 static gboolean
245 gdk_frame_clock_flush_idle (void *data)
246 {
247   GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
248   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
249   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
250
251   priv->flush_idle_id = 0;
252
253   if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE)
254     return FALSE;
255
256   priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
257   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
258
259   g_signal_emit_by_name (G_OBJECT (clock), "flush-events");
260
261   if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
262     priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
263   else
264     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
265
266   return FALSE;
267 }
268
269 static gboolean
270 gdk_frame_clock_paint_idle (void *data)
271 {
272   GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
273   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
274   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
275   gboolean skip_to_resume_events;
276   GdkFrameTimings *timings = NULL;
277
278   priv->paint_idle_id = 0;
279   priv->in_paint_idle = TRUE;
280   priv->min_next_frame_time = 0;
281
282   skip_to_resume_events =
283     (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0;
284
285   if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
286     {
287       timings = gdk_frame_clock_get_current_timings (clock);
288     }
289
290   if (!skip_to_resume_events)
291     {
292       switch (priv->phase)
293         {
294         case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS:
295           break;
296         case GDK_FRAME_CLOCK_PHASE_NONE:
297         case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
298           if (priv->freeze_count == 0)
299             {
300               priv->frame_time = compute_frame_time (clock_idle);
301
302               _gdk_frame_clock_begin_frame (clock);
303               timings = gdk_frame_clock_get_current_timings (clock);
304
305               timings->frame_time = priv->frame_time;
306               timings->slept_before = priv->sleep_serial != get_sleep_serial ();
307
308               priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
309
310               /* We always emit ::before-paint and ::after-paint if
311                * any of the intermediate phases are requested and
312                * they don't get repeated if you freeze/thaw while
313                * in them. */
314               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
315               g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
316               priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
317             }
318         case GDK_FRAME_CLOCK_PHASE_UPDATE:
319           if (priv->freeze_count == 0)
320             {
321               if (priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE)
322                 {
323                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
324                   g_signal_emit_by_name (G_OBJECT (clock), "update");
325                 }
326             }
327         case GDK_FRAME_CLOCK_PHASE_LAYOUT:
328           if (priv->freeze_count == 0)
329             {
330 #ifdef G_ENABLE_DEBUG
331               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
332                 {
333                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
334                       (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
335                     timings->layout_start_time = g_get_monotonic_time ();
336                 }
337 #endif /* G_ENABLE_DEBUG */
338
339               priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
340               if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
341                 {
342                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
343                   g_signal_emit_by_name (G_OBJECT (clock), "layout");
344                 }
345             }
346         case GDK_FRAME_CLOCK_PHASE_PAINT:
347           if (priv->freeze_count == 0)
348             {
349 #ifdef G_ENABLE_DEBUG
350               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
351                 {
352                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
353                       (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
354                     timings->paint_start_time = g_get_monotonic_time ();
355                 }
356 #endif /* G_ENABLE_DEBUG */
357
358               priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
359               if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
360                 {
361                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
362                   g_signal_emit_by_name (G_OBJECT (clock), "paint");
363                 }
364             }
365         case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
366           if (priv->freeze_count == 0)
367             {
368               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
369               g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
370               /* the ::after-paint phase doesn't get repeated on freeze/thaw,
371                */
372               priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
373
374 #ifdef G_ENABLE_DEBUG
375               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
376                 timings->frame_end_time = g_get_monotonic_time ();
377 #endif /* G_ENABLE_DEBUG */
378             }
379         case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
380           ;
381         }
382     }
383
384 #ifdef G_ENABLE_DEBUG
385   if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
386     {
387       if (timings->complete)
388         _gdk_frame_clock_debug_print_timings (clock, timings);
389     }
390 #endif /* G_ENABLE_DEBUG */
391
392   if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
393     {
394       priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
395       g_signal_emit_by_name (G_OBJECT (clock), "resume-events");
396     }
397
398   if (priv->freeze_count == 0)
399     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
400
401   priv->in_paint_idle = FALSE;
402
403   /* If there is throttling in the backend layer, then we'll do another
404    * update as soon as the backend unthrottles (if there is work to do),
405    * otherwise we need to figure when the next frame should be.
406    */
407   if (priv->freeze_count == 0)
408     {
409       priv->min_next_frame_time = compute_min_next_frame_time (clock_idle,
410                                                                priv->frame_time);
411       maybe_start_idle (clock_idle);
412     }
413
414   if (priv->freeze_count == 0)
415     priv->sleep_serial = get_sleep_serial ();
416
417   return FALSE;
418 }
419
420 static void
421 gdk_frame_clock_idle_request_phase (GdkFrameClock      *clock,
422                                     GdkFrameClockPhase  phase)
423 {
424   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
425   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
426
427   priv->requested |= phase;
428   maybe_start_idle (clock_idle);
429 }
430
431 static void
432 gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
433 {
434   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
435
436   priv->freeze_count++;
437
438   if (priv->freeze_count == 1)
439     {
440       if (priv->flush_idle_id)
441         {
442           g_source_remove (priv->flush_idle_id);
443           priv->flush_idle_id = 0;
444         }
445       if (priv->paint_idle_id)
446         {
447           g_source_remove (priv->paint_idle_id);
448           priv->paint_idle_id = 0;
449         }
450     }
451 }
452
453 static void
454 gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
455 {
456   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
457   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
458
459   g_return_if_fail (priv->freeze_count > 0);
460
461   priv->freeze_count--;
462   if (priv->freeze_count == 0)
463     {
464       maybe_start_idle (clock_idle);
465       /* If nothing is requested so we didn't start an idle, we need
466        * to skip to the end of the state chain, since the idle won't
467        * run and do it for us. */
468       if (priv->paint_idle_id == 0)
469         priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
470
471       priv->sleep_serial = get_sleep_serial ();
472     }
473 }
474
475 static void
476 gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
477 {
478   GObjectClass *gobject_class = (GObjectClass*) klass;
479   GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
480
481   gobject_class->finalize     = gdk_frame_clock_idle_finalize;
482
483   frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
484   frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
485   frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
486   frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
487
488   g_type_class_add_private (klass, sizeof (GdkFrameClockIdlePrivate));
489 }
490
491 GdkFrameClock *
492 _gdk_frame_clock_idle_new (void)
493 {
494   GdkFrameClockIdle *clock;
495
496   clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
497
498   return GDK_FRAME_CLOCK (clock);
499 }