]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclockidle.c
da05a9698acdb7ce0d3f4a892a86d13aa4f6816c
[~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   guint64 timer_base;
41   guint64 frame_time;
42   guint64 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 guint64
139 compute_frame_time (GdkFrameClockIdle *idle)
140 {
141   GdkFrameClockIdlePrivate *priv = idle->priv;
142   guint64 computed_frame_time;
143   guint64 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 guint64
164 gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
165 {
166   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
167   guint64 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           guint64 now = compute_frame_time (clock_idle);
200           guint64 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           g_signal_emit_by_name (clock_idle, "frame-requested");
225         }
226     }
227 }
228
229 static gint64
230 compute_min_next_frame_time (GdkFrameClockIdle *clock_idle,
231                              gint64             last_frame_time)
232 {
233   gint64 presentation_time;
234   gint64 refresh_interval;
235
236   gdk_frame_clock_get_refresh_info (GDK_FRAME_CLOCK (clock_idle),
237                                     last_frame_time,
238                                     &refresh_interval, &presentation_time);
239
240   if (presentation_time == 0)
241     return last_frame_time + refresh_interval;
242   else
243     return presentation_time + refresh_interval / 2;
244 }
245
246 static gboolean
247 gdk_frame_clock_flush_idle (void *data)
248 {
249   GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
250   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
251   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
252
253   priv->flush_idle_id = 0;
254
255   if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE)
256     return FALSE;
257
258   priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
259   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
260
261   g_signal_emit_by_name (G_OBJECT (clock), "flush-events");
262
263   if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
264     priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
265   else
266     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
267
268   return FALSE;
269 }
270
271 static gboolean
272 gdk_frame_clock_paint_idle (void *data)
273 {
274   GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
275   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
276   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
277   gboolean skip_to_resume_events;
278   GdkFrameTimings *timings = NULL;
279
280   priv->paint_idle_id = 0;
281   priv->in_paint_idle = TRUE;
282   priv->min_next_frame_time = 0;
283
284   skip_to_resume_events =
285     (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0;
286
287   if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
288     {
289       timings = gdk_frame_clock_get_current_frame_timings (clock);
290     }
291
292   if (!skip_to_resume_events)
293     {
294       switch (priv->phase)
295         {
296         case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS:
297           break;
298         case GDK_FRAME_CLOCK_PHASE_NONE:
299         case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
300           if (priv->freeze_count == 0)
301             {
302               priv->frame_time = compute_frame_time (clock_idle);
303
304               _gdk_frame_clock_begin_frame (clock);
305               timings = gdk_frame_clock_get_current_frame_timings (clock);
306
307               gdk_frame_timings_set_frame_time (timings, priv->frame_time);
308
309               gdk_frame_timings_set_slept_before (timings,
310                                                   priv->sleep_serial != get_sleep_serial ());
311
312               priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
313
314               /* We always emit ::before-paint and ::after-paint if
315                * any of the intermediate phases are requested and
316                * they don't get repeated if you freeze/thaw while
317                * in them. */
318               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
319               g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
320               priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
321             }
322         case GDK_FRAME_CLOCK_PHASE_UPDATE:
323           if (priv->freeze_count == 0)
324             {
325               if (priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE)
326                 {
327                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
328                   g_signal_emit_by_name (G_OBJECT (clock), "update");
329                 }
330             }
331         case GDK_FRAME_CLOCK_PHASE_LAYOUT:
332           if (priv->freeze_count == 0)
333             {
334 #ifdef G_ENABLE_DEBUG
335               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
336                 {
337                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
338                       (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
339                     _gdk_frame_timings_set_layout_start_time (timings, g_get_monotonic_time ());
340                 }
341 #endif /* G_ENABLE_DEBUG */
342
343               priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
344               if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
345                 {
346                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
347                   g_signal_emit_by_name (G_OBJECT (clock), "layout");
348                 }
349             }
350         case GDK_FRAME_CLOCK_PHASE_PAINT:
351           if (priv->freeze_count == 0)
352             {
353 #ifdef G_ENABLE_DEBUG
354               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
355                 {
356                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
357                       (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
358                     _gdk_frame_timings_set_paint_start_time (timings, g_get_monotonic_time ());
359                 }
360 #endif /* G_ENABLE_DEBUG */
361
362               priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
363               if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
364                 {
365                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
366                   g_signal_emit_by_name (G_OBJECT (clock), "paint");
367                 }
368             }
369         case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
370           if (priv->freeze_count == 0)
371             {
372               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
373               g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
374               /* the ::after-paint phase doesn't get repeated on freeze/thaw,
375                */
376               priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
377
378 #ifdef G_ENABLE_DEBUG
379               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
380                 _gdk_frame_timings_set_frame_end_time (timings, g_get_monotonic_time ());
381 #endif /* G_ENABLE_DEBUG */
382             }
383         case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
384           ;
385         }
386     }
387
388 #ifdef G_ENABLE_DEBUG
389   if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
390     {
391       if (gdk_frame_timings_get_complete (timings))
392         _gdk_frame_clock_debug_print_timings (clock, timings);
393     }
394 #endif /* G_ENABLE_DEBUG */
395
396   if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
397     {
398       priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
399       g_signal_emit_by_name (G_OBJECT (clock), "resume-events");
400     }
401
402   if (priv->freeze_count == 0)
403     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
404
405   priv->in_paint_idle = FALSE;
406
407   /* If there is throttling in the backend layer, then we'll do another
408    * update as soon as the backend unthrottles (if there is work to do),
409    * otherwise we need to figure when the next frame should be.
410    */
411   if (priv->freeze_count == 0)
412     {
413       priv->min_next_frame_time = compute_min_next_frame_time (clock_idle,
414                                                                priv->frame_time);
415       maybe_start_idle (clock_idle);
416     }
417
418   if (priv->freeze_count == 0)
419     priv->sleep_serial = get_sleep_serial ();
420
421   return FALSE;
422 }
423
424 static void
425 gdk_frame_clock_idle_request_phase (GdkFrameClock      *clock,
426                                     GdkFrameClockPhase  phase)
427 {
428   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
429   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
430
431   priv->requested |= phase;
432   maybe_start_idle (clock_idle);
433 }
434
435 static GdkFrameClockPhase
436 gdk_frame_clock_idle_get_requested (GdkFrameClock *clock)
437 {
438   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
439
440   return priv->requested;
441 }
442
443 static void
444 gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
445 {
446   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
447
448   priv->freeze_count++;
449
450   if (priv->freeze_count == 1)
451     {
452       if (priv->flush_idle_id)
453         {
454           g_source_remove (priv->flush_idle_id);
455           priv->flush_idle_id = 0;
456         }
457       if (priv->paint_idle_id)
458         {
459           g_source_remove (priv->paint_idle_id);
460           priv->paint_idle_id = 0;
461         }
462     }
463 }
464
465 static void
466 gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
467 {
468   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
469   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
470
471   g_return_if_fail (priv->freeze_count > 0);
472
473   priv->freeze_count--;
474   if (priv->freeze_count == 0)
475     {
476       maybe_start_idle (clock_idle);
477       /* If nothing is requested so we didn't start an idle, we need
478        * to skip to the end of the state chain, since the idle won't
479        * run and do it for us. */
480       if (priv->paint_idle_id == 0)
481         priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
482
483       priv->sleep_serial = get_sleep_serial ();
484     }
485 }
486
487 static void
488 gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
489 {
490   GObjectClass *gobject_class = (GObjectClass*) klass;
491   GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
492
493   gobject_class->finalize     = gdk_frame_clock_idle_finalize;
494
495   frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
496   frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
497   frame_clock_class->get_requested = gdk_frame_clock_idle_get_requested;
498   frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
499   frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
500
501   g_type_class_add_private (klass, sizeof (GdkFrameClockIdlePrivate));
502 }
503
504 GdkFrameClock *
505 _gdk_frame_clock_idle_new (void)
506 {
507   GdkFrameClockIdle *clock;
508
509   clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
510
511   return GDK_FRAME_CLOCK (clock);
512 }