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, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gdkinternals.h"
28#include "gdkframeclockprivate.h"
29#include "gdkframeclockidle.h"
30#include "gdk.h"
31
32#ifdef G_OS_WIN32
33#include <windows.h>
34#endif
35
36#define FRAME_INTERVAL 16667 /* microseconds */
37
38struct _GdkFrameClockIdlePrivate
39{
40 GTimer *timer;
41 /* timer_base is used to avoid ever going backward */
42 gint64 timer_base;
43 gint64 frame_time;
44 gint64 min_next_frame_time;
45 gint64 sleep_serial;
46
47 guint flush_idle_id;
48 guint paint_idle_id;
49 guint freeze_count;
50 guint updating_count;
51
52 GdkFrameClockPhase requested;
53 GdkFrameClockPhase phase;
54
55 guint in_paint_idle : 1;
56#ifdef G_OS_WIN32
57 guint begin_period : 1;
58#endif
59};
60
61static gboolean gdk_frame_clock_flush_idle (void *data);
62static gboolean gdk_frame_clock_paint_idle (void *data);
63
64G_DEFINE_TYPE_WITH_PRIVATE (GdkFrameClockIdle, gdk_frame_clock_idle, GDK_TYPE_FRAME_CLOCK)
65
66static gint64 sleep_serial;
67static gint64 sleep_source_prepare_time;
68static GSource *sleep_source;
69
70static gboolean
71sleep_source_prepare (GSource *source,
72 gint *timeout)
73{
74 sleep_source_prepare_time = g_source_get_time (source);
75 *timeout = -1;
76 return FALSE;
77}
78
79static gboolean
80sleep_source_check (GSource *source)
81{
82 if (g_source_get_time (source) != sleep_source_prepare_time)
83 sleep_serial++;
84
85 return FALSE;
86}
87
88static gboolean
89sleep_source_dispatch (GSource *source,
90 GSourceFunc callback,
91 gpointer user_data)
92{
93 return TRUE;
94}
95
96static GSourceFuncs sleep_source_funcs = {
97 sleep_source_prepare,
98 sleep_source_check,
99 sleep_source_dispatch,
100 NULL /* finalize */
101};
102
103static gint64
104get_sleep_serial (void)
105{
106 if (sleep_source == NULL)
107 {
108 sleep_source = g_source_new (&sleep_source_funcs, sizeof (GSource));
109
110 g_source_set_priority (sleep_source, G_PRIORITY_HIGH);
111 g_source_attach (sleep_source, NULL);
112 g_source_unref (sleep_source);
113 }
114
115 return sleep_serial;
116}
117
118static void
119gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
120{
121 GdkFrameClockIdlePrivate *priv;
122
123 frame_clock_idle->priv = priv =
124 gdk_frame_clock_idle_get_instance_private (frame_clock_idle);
125
126 priv->freeze_count = 0;
127}
128
129static void
130gdk_frame_clock_idle_dispose (GObject *object)
131{
132 GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (object)->priv;
133
134 if (priv->flush_idle_id != 0)
135 {
136 g_source_remove (priv->flush_idle_id);
137 priv->flush_idle_id = 0;
138 }
139
140 if (priv->paint_idle_id != 0)
141 {
142 g_source_remove (priv->paint_idle_id);
143 priv->paint_idle_id = 0;
144 }
145
146#ifdef G_OS_WIN32
147 if (priv->begin_period)
148 {
149 timeEndPeriod(1);
150 priv->begin_period = FALSE;
151 }
152#endif
153
154 G_OBJECT_CLASS (gdk_frame_clock_idle_parent_class)->dispose (object);
155}
156
157static gint64
158compute_frame_time (GdkFrameClockIdle *idle)
159{
160 GdkFrameClockIdlePrivate *priv = idle->priv;
161 gint64 computed_frame_time;
162 gint64 elapsed;
163
164 elapsed = g_get_monotonic_time () + priv->timer_base;
165 if (elapsed < priv->frame_time)
166 {
167 /* clock went backward. adapt to that by forevermore increasing
168 * timer_base. For now, assume we've gone forward in time 1ms.
169 */
170 /* hmm. just fix GTimer? */
171 computed_frame_time = priv->frame_time + 1;
172 priv->timer_base += (priv->frame_time - elapsed) + 1;
173 }
174 else
175 {
176 computed_frame_time = elapsed;
177 }
178
179 return computed_frame_time;
180}
181
182static gint64
183gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
184{
185 GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
186 gint64 computed_frame_time;
187
188 /* can't change frame time during a paint */
189 if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE &&
190 priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS)
191 return priv->frame_time;
192
193 /* Outside a paint, pick something close to "now" */
194 computed_frame_time = compute_frame_time (GDK_FRAME_CLOCK_IDLE (clock));
195
196 /* 16ms is 60fps. We only update frame time that often because we'd
197 * like to try to keep animations on the same start times.
198 * get_frame_time() would normally be used outside of a paint to
199 * record an animation start time for example.
200 */
201 if ((computed_frame_time - priv->frame_time) > FRAME_INTERVAL)
202 priv->frame_time = computed_frame_time;
203
204 return priv->frame_time;
205}
206
207#define RUN_FLUSH_IDLE(priv) \
208 ((priv)->freeze_count == 0 && \
209 ((priv)->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0)
210
211/* The reason why we track updating_count separately here and don't
212 * just add GDK_FRAME_CLOCK_PHASE_UPDATE into ->request on every frame
213 * is so that we can avoid doing one more frame when an animation
214 * is cancelled.
215 */
216#define RUN_PAINT_IDLE(priv) \
217 ((priv)->freeze_count == 0 && \
218 (((priv)->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 || \
219 (priv)->updating_count > 0))
220
221static void
222maybe_start_idle (GdkFrameClockIdle *clock_idle)
223{
224 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
225
226 if (RUN_FLUSH_IDLE (priv) || RUN_PAINT_IDLE (priv))
227 {
228 guint min_interval = 0;
229
230 if (priv->min_next_frame_time != 0)
231 {
232 gint64 now = compute_frame_time (clock_idle);
233 gint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now;
234 min_interval = (min_interval_us + 500) / 1000;
235 }
236
237 if (priv->flush_idle_id == 0 && RUN_FLUSH_IDLE (priv))
238 {
239 priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1,
240 min_interval,
241 gdk_frame_clock_flush_idle,
242 g_object_ref (clock_idle),
243 (GDestroyNotify) g_object_unref);
244 g_source_set_name_by_id (priv->flush_idle_id, "[gtk+] gdk_frame_clock_flush_idle");
245 }
246
247 if (!priv->in_paint_idle &&
248 priv->paint_idle_id == 0 && RUN_PAINT_IDLE (priv))
249 {
250 priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW,
251 min_interval,
252 gdk_frame_clock_paint_idle,
253 g_object_ref (clock_idle),
254 (GDestroyNotify) g_object_unref);
255 g_source_set_name_by_id (priv->paint_idle_id, "[gtk+] gdk_frame_clock_paint_idle");
256 }
257 }
258}
259
260static void
261maybe_stop_idle (GdkFrameClockIdle *clock_idle)
262{
263 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
264
265 if (priv->flush_idle_id != 0 && !RUN_FLUSH_IDLE (priv))
266 {
267 g_source_remove (priv->flush_idle_id);
268 priv->flush_idle_id = 0;
269 }
270
271 if (priv->paint_idle_id != 0 && !RUN_PAINT_IDLE (priv))
272 {
273 g_source_remove (priv->paint_idle_id);
274 priv->paint_idle_id = 0;
275 }
276}
277
278static gint64
279compute_min_next_frame_time (GdkFrameClockIdle *clock_idle,
280 gint64 last_frame_time)
281{
282 gint64 presentation_time;
283 gint64 refresh_interval;
284
285 gdk_frame_clock_get_refresh_info (GDK_FRAME_CLOCK (clock_idle),
286 last_frame_time,
287 &refresh_interval, &presentation_time);
288
289 if (presentation_time == 0)
290 return last_frame_time + refresh_interval;
291 else
292 return presentation_time + refresh_interval / 2;
293}
294
295static gboolean
296gdk_frame_clock_flush_idle (void *data)
297{
298 GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
299 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
300 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
301
302 priv->flush_idle_id = 0;
303
304 if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE)
305 return FALSE;
306
307 priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
308 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS;
309
310 _gdk_frame_clock_emit_flush_events (clock);
311
312 if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0 ||
313 priv->updating_count > 0)
314 priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
315 else
316 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
317
318 return FALSE;
319}
320
321static gboolean
322gdk_frame_clock_paint_idle (void *data)
323{
324 GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
325 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
326 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
327 gboolean skip_to_resume_events;
328 GdkFrameTimings *timings = NULL;
329
330 priv->paint_idle_id = 0;
331 priv->in_paint_idle = TRUE;
332 priv->min_next_frame_time = 0;
333
334 skip_to_resume_events =
335 (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0 &&
336 priv->updating_count == 0;
337
338 if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT)
339 {
340 timings = gdk_frame_clock_get_current_timings (clock);
341 }
342
343 if (!skip_to_resume_events)
344 {
345 switch (priv->phase)
346 {
347 case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS:
348 break;
349 case GDK_FRAME_CLOCK_PHASE_NONE:
350 case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
351 if (priv->freeze_count == 0)
352 {
353 priv->frame_time = compute_frame_time (clock_idle);
354
355 _gdk_frame_clock_begin_frame (clock);
356 timings = gdk_frame_clock_get_current_timings (clock);
357
358 timings->frame_time = priv->frame_time;
359 timings->slept_before = priv->sleep_serial != get_sleep_serial ();
360
361 priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
362
363 /* We always emit ::before-paint and ::after-paint if
364 * any of the intermediate phases are requested and
365 * they don't get repeated if you freeze/thaw while
366 * in them.
367 */
368 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
369 _gdk_frame_clock_emit_before_paint (clock);
370 priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE;
371 }
372 /* fallthrough */
373 case GDK_FRAME_CLOCK_PHASE_UPDATE:
374 if (priv->freeze_count == 0)
375 {
376 if ((priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE) != 0 ||
377 priv->updating_count > 0)
378 {
379 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE;
380 _gdk_frame_clock_emit_update (clock);
381 }
382 }
383 /* fallthrough */
384 case GDK_FRAME_CLOCK_PHASE_LAYOUT:
385 if (priv->freeze_count == 0)
386 {
387 int iter;
388#ifdef G_ENABLE_DEBUG
389 if (GDK_DEBUG_CHECK (FRAMES))
390 {
391 if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT &&
392 (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT))
393 timings->layout_start_time = g_get_monotonic_time ();
394 }
395#endif /* G_ENABLE_DEBUG */
396
397 priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
398 /* We loop in the layout phase, because we don't want to progress
399 * into the paint phase with invalid size allocations. This may
400 * happen in some situation like races between user window
401 * resizes and natural size changes.
402 */
403 iter = 0;
404 while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) &&
405 priv->freeze_count == 0 && iter++ < 4)
406 {
407 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
408 _gdk_frame_clock_emit_layout (clock);
409 }
410 if (iter == 5)
411 g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries");
412 }
413 /* fallthrough */
414 case GDK_FRAME_CLOCK_PHASE_PAINT:
415 if (priv->freeze_count == 0)
416 {
417#ifdef G_ENABLE_DEBUG
418 if (GDK_DEBUG_CHECK (FRAMES))
419 {
420 if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT &&
421 (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT))
422 timings->paint_start_time = g_get_monotonic_time ();
423 }
424#endif /* G_ENABLE_DEBUG */
425
426 priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
427 if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
428 {
429 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
430 _gdk_frame_clock_emit_paint (clock);
431 }
432 }
433 /* fallthrough */
434 case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
435 if (priv->freeze_count == 0)
436 {
437 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
438 _gdk_frame_clock_emit_after_paint (clock);
439 /* the ::after-paint phase doesn't get repeated on freeze/thaw,
440 */
441 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
442
443#ifdef G_ENABLE_DEBUG
444 if (GDK_DEBUG_CHECK (FRAMES))
445 timings->frame_end_time = g_get_monotonic_time ();
446#endif /* G_ENABLE_DEBUG */
447 }
448 /* fallthrough */
449 case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS:
450 ;
451 }
452 }
453
454#ifdef G_ENABLE_DEBUG
455 if (GDK_DEBUG_CHECK (FRAMES))
456 {
457 if (timings && timings->complete)
458 _gdk_frame_clock_debug_print_timings (clock, timings);
459 }
460#endif /* G_ENABLE_DEBUG */
461
462 if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)
463 {
464 priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS;
465 _gdk_frame_clock_emit_resume_events (clock);
466 }
467
468 if (priv->freeze_count == 0)
469 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
470
471 priv->in_paint_idle = FALSE;
472
473 /* If there is throttling in the backend layer, then we'll do another
474 * update as soon as the backend unthrottles (if there is work to do),
475 * otherwise we need to figure when the next frame should be.
476 */
477 if (priv->freeze_count == 0)
478 {
479 priv->min_next_frame_time = compute_min_next_frame_time (clock_idle,
480 priv->frame_time);
481 maybe_start_idle (clock_idle);
482 }
483
484 if (priv->freeze_count == 0)
485 priv->sleep_serial = get_sleep_serial ();
486
487 return FALSE;
488}
489
490static void
491gdk_frame_clock_idle_request_phase (GdkFrameClock *clock,
492 GdkFrameClockPhase phase)
493{
494 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
495 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
496
497 priv->requested |= phase;
498 maybe_start_idle (clock_idle);
499}
500
501static void
502gdk_frame_clock_idle_begin_updating (GdkFrameClock *clock)
503{
504 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
505 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
506
507#ifdef G_OS_WIN32
508 /* We need a higher resolution timer while doing animations */
509 if (priv->updating_count == 0 && !priv->begin_period)
510 {
511 timeBeginPeriod(1);
512 priv->begin_period = TRUE;
513 }
514#endif
515
516 priv->updating_count++;
517 maybe_start_idle (clock_idle);
518}
519
520static void
521gdk_frame_clock_idle_end_updating (GdkFrameClock *clock)
522{
523 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
524 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
525
526 g_return_if_fail (priv->updating_count > 0);
527
528 priv->updating_count--;
529 maybe_stop_idle (clock_idle);
530
531#ifdef G_OS_WIN32
532 if (priv->updating_count == 0 && priv->begin_period)
533 {
534 timeEndPeriod(1);
535 priv->begin_period = FALSE;
536 }
537#endif
538}
539
540static void
541gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
542{
543 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
544 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
545
546 priv->freeze_count++;
547 maybe_stop_idle (clock_idle);
548}
549
550static void
551gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
552{
553 GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
554 GdkFrameClockIdlePrivate *priv = clock_idle->priv;
555
556 g_return_if_fail (priv->freeze_count > 0);
557
558 priv->freeze_count--;
559 if (priv->freeze_count == 0)
560 {
561 maybe_start_idle (clock_idle);
562 /* If nothing is requested so we didn't start an idle, we need
563 * to skip to the end of the state chain, since the idle won't
564 * run and do it for us.
565 */
566 if (priv->paint_idle_id == 0)
567 priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
568
569 priv->sleep_serial = get_sleep_serial ();
570 }
571}
572
573static void
574gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
575{
576 GObjectClass *gobject_class = (GObjectClass*) klass;
577 GdkFrameClockClass *frame_clock_class = (GdkFrameClockClass *)klass;
578
579 gobject_class->dispose = gdk_frame_clock_idle_dispose;
580
581 frame_clock_class->get_frame_time = gdk_frame_clock_idle_get_frame_time;
582 frame_clock_class->request_phase = gdk_frame_clock_idle_request_phase;
583 frame_clock_class->begin_updating = gdk_frame_clock_idle_begin_updating;
584 frame_clock_class->end_updating = gdk_frame_clock_idle_end_updating;
585 frame_clock_class->freeze = gdk_frame_clock_idle_freeze;
586 frame_clock_class->thaw = gdk_frame_clock_idle_thaw;
587}
588
589GdkFrameClock *
590_gdk_frame_clock_idle_new (void)
591{
592 GdkFrameClockIdle *clock;
593
594 clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
595
596 return GDK_FRAME_CLOCK (clock);
597}
598