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