]> Pileus Git - ~andy/gtk/blob - gdk/gdkframeclockidle.c
afb9430945fb1a5276e2dfbe2a884ce6e3a92864
[~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               timings->frame_time = priv->frame_time;
308               timings->slept_before = priv->sleep_serial != get_sleep_serial ();
309
310               priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
311
312               /* We always emit ::before-paint and ::after-paint if
313                * any of the intermediate phases are requested and
314                * they don't get repeated if you freeze/thaw while
315                * in them. */
316               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
317               g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
318               priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
319             }
320         case GDK_FRAME_CLOCK_PHASE_UPDATE:
321           if (priv->freeze_count == 0)
322             {
323               if (priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE)
324                 {
325                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
326                   g_signal_emit_by_name (G_OBJECT (clock), "update");
327                 }
328             }
329         case GDK_FRAME_CLOCK_PHASE_LAYOUT:
330           if (priv->freeze_count == 0)
331             {
332 #ifdef G_ENABLE_DEBUG
333               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
334                 {
335                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
336                       (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
337                     timings->layout_start_time = g_get_monotonic_time ();
338                 }
339 #endif /* G_ENABLE_DEBUG */
340
341               priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
342               if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
343                 {
344                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
345                   g_signal_emit_by_name (G_OBJECT (clock), "layout");
346                 }
347             }
348         case GDK_FRAME_CLOCK_PHASE_PAINT:
349           if (priv->freeze_count == 0)
350             {
351 #ifdef G_ENABLE_DEBUG
352               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
353                 {
354                   if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
355                       (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
356                     timings->paint_start_time = g_get_monotonic_time ();
357                 }
358 #endif /* G_ENABLE_DEBUG */
359
360               priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
361               if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
362                 {
363                   priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
364                   g_signal_emit_by_name (G_OBJECT (clock), "paint");
365                 }
366             }
367         case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
368           if (priv->freeze_count == 0)
369             {
370               priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
371               g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
372               /* the ::after-paint phase doesn't get repeated on freeze/thaw,
373                */
374               priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
375
376 #ifdef G_ENABLE_DEBUG
377               if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
378                 timings->frame_end_time = g_get_monotonic_time ();
379 #endif /* G_ENABLE_DEBUG */
380             }
381         case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
382           ;
383         }
384     }
385
386 #ifdef G_ENABLE_DEBUG
387   if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
388     {
389       if (timings->complete)
390         _gdk_frame_clock_debug_print_timings (clock, timings);
391     }
392 #endif /* G_ENABLE_DEBUG */
393
394   if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
395     {
396       priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
397       g_signal_emit_by_name (G_OBJECT (clock), "resume-events");
398     }
399
400   if (priv->freeze_count == 0)
401     priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
402
403   priv->in_paint_idle = FALSE;
404
405   /* If there is throttling in the backend layer, then we'll do another
406    * update as soon as the backend unthrottles (if there is work to do),
407    * otherwise we need to figure when the next frame should be.
408    */
409   if (priv->freeze_count == 0)
410     {
411       priv->min_next_frame_time = compute_min_next_frame_time (clock_idle,
412                                                                priv->frame_time);
413       maybe_start_idle (clock_idle);
414     }
415
416   if (priv->freeze_count == 0)
417     priv->sleep_serial = get_sleep_serial ();
418
419   return FALSE;
420 }
421
422 static void
423 gdk_frame_clock_idle_request_phase (GdkFrameClock      *clock,
424                                     GdkFrameClockPhase  phase)
425 {
426   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
427   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
428
429   priv->requested |= phase;
430   maybe_start_idle (clock_idle);
431 }
432
433 static GdkFrameClockPhase
434 gdk_frame_clock_idle_get_requested (GdkFrameClock *clock)
435 {
436   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
437
438   return priv->requested;
439 }
440
441 static void
442 gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
443 {
444   GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
445
446   priv->freeze_count++;
447
448   if (priv->freeze_count == 1)
449     {
450       if (priv->flush_idle_id)
451         {
452           g_source_remove (priv->flush_idle_id);
453           priv->flush_idle_id = 0;
454         }
455       if (priv->paint_idle_id)
456         {
457           g_source_remove (priv->paint_idle_id);
458           priv->paint_idle_id = 0;
459         }
460     }
461 }
462
463 static void
464 gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
465 {
466   GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
467   GdkFrameClockIdlePrivate *priv = clock_idle->priv;
468
469   g_return_if_fail (priv->freeze_count > 0);
470
471   priv->freeze_count--;
472   if (priv->freeze_count == 0)
473     {
474       maybe_start_idle (clock_idle);
475       /* If nothing is requested so we didn't start an idle, we need
476        * to skip to the end of the state chain, since the idle won't
477        * run and do it for us. */
478       if (priv->paint_idle_id == 0)
479         priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
480
481       priv->sleep_serial = get_sleep_serial ();
482     }
483 }
484
485 static void
486 gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
487 {
488   GObjectClass *gobject_class = (GObjectClass*) klass;
489   GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
490
491   gobject_class->finalize     = gdk_frame_clock_idle_finalize;
492
493   frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
494   frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
495   frame_clock_class->get_requested = gdk_frame_clock_idle_get_requested;
496   frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
497   frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
498
499   g_type_class_add_private (klass, sizeof (GdkFrameClockIdlePrivate));
500 }
501
502 GdkFrameClock *
503 _gdk_frame_clock_idle_new (void)
504 {
505   GdkFrameClockIdle *clock;
506
507   clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
508
509   return GDK_FRAME_CLOCK (clock);
510 }