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